1#!/bin/bash -eu
2# Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
3#
4# Licensed under the Apache License 2.0 (the "License").
5# You may not use this file except in compliance with the License.
6# You can obtain a copy in the file LICENSE in the source distribution
7# or at https://www.openssl.org/source/license.html
8#
9# Script to analyze logs produced by REPORT_RWLOCK_CONTENTION
10# Usage: ./analyze-contention-log.sh <logfile>
11###################################################################
12
13#
14# Setup a temp directory to massage our log file
15#
16TEMPDIR=$(mktemp -d /tmp/contention.XXXXXX)
17
18trap "rm -rf $TEMPDIR" EXIT
19
20echo "Splitting files" > /dev/stderr
21
22#
23#start by splitting the log into separate stack traces
24#
25mkdir "$TEMPDIR/individual_files"
26cat "$@" > "$TEMPDIR/individual_files/log"
27pushd $TEMPDIR/individual_files/ > /dev/null
28awk '
29    BEGIN {RS = ""; FS = "\n"}
30    {file_num++; print > ("stacktrace" file_num ".txt")}' ./log
31popd > /dev/null
32rm -f $TEMPDIR/individual_files/log
33
34#
35# Make some associative arrays to track our stats
36#
37declare -A filenames
38declare -A total_latency
39declare -A latency_counts
40
41echo "Gathering latencies" > /dev/stderr
42FILECOUNT=$(ls $TEMPDIR/individual_files/stacktrace*.* | wc -l)
43currentidx=0
44
45#
46# Look at every stack trace, get and record its latency, and hash value
47#
48for i in $(ls $TEMPDIR/individual_files/stacktrace*.*)
49do
50    LATENCY=$(awk '{print $6}' $i)
51    #drop the non-stacktrace line
52    sed -i -e"s/lock blocked on.*//" $i
53    #now compute its sha1sum
54    SHA1SUM=$(sha1sum $i | awk '{print $1}')
55    filenames["$SHA1SUM"]=$i
56    CUR_LATENCY=0
57    LATENCY_COUNT=0
58
59    #
60    # If we already have a latency total for this hash value
61    # fetch it from the total_latency array, along with
62    # the number of times we've encountered this hash
63    #
64    if [[ -v total_latency["$SHA1SUM"] ]]
65    then
66        CUR_LATENCY=${total_latency["$SHA1SUM"]}
67        LATENCY_COUNT=${latency_counts["$SHA1SUM"]}
68    fi
69
70    #
71    # Add this files latency to the hashes total latency amount
72    # and increment its associated count by 1
73    #
74    total_latency["$SHA1SUM"]=$(dc -e "$CUR_LATENCY $LATENCY + p")
75    latency_counts["$SHA1SUM"]=$(dc -e "$LATENCY_COUNT 1 + p")
76    echo -e -n "FILE $currentidx/$FILECOUNT \r" > /dev/stderr
77    currentidx=$((currentidx + 1))
78done
79
80#
81# Write out each latency in the hash array to a file named after its total latency
82#
83mkdir $TEMPDIR/sorted_latencies/
84for i in ${!total_latency[@]}
85do
86    TOTAL=${total_latency[$i]}
87    COUNT=${latency_counts[$i]}
88    FNAME=${filenames[$i]}
89    AVG=$(dc -e "6 k $TOTAL $COUNT / p")
90    echo "Total latency $TOTAL usec, count $COUNT (avg $AVG usec)" >> $TEMPDIR/sorted_latencies/$TOTAL.txt
91    cat $FNAME >> $TEMPDIR/sorted_latencies/$TOTAL.txt
92done
93
94#
95# Now because we have our cumulative latencies recorded in files named
96# after their total cumulative latency, we can easily do a numerical
97# sort on them in reverse order to display them from greatest to least
98#
99echo "Top latencies"
100for i in $(ls $TEMPDIR/sorted_latencies/ | sort -n -r)
101do
102    echo "============================================="
103    cat $TEMPDIR/sorted_latencies/$i
104    echo ""
105done
106
107