summary.py 8.39 KB
Newer Older
1 2 3
#!/usr/bin/env python3
# -*- coding:utf-8 -*-

Mario Hock's avatar
Mario Hock committed
4 5 6 7 8 9 10 11 12
# Copyright (c) 2014,
# Karlsruhe Institute of Technology, Institute of Telematics
#
# This code is provided under the BSD 2-Clause License.
# Please refer to the LICENSE.txt file for further information.
#
# Author: Mario Hock


13
import time
Mario Hock's avatar
Mario Hock committed
14
import os
15
from itertools import zip_longest
Mario Hock's avatar
Mario Hock committed
16

Mario Hock's avatar
Mario Hock committed
17
from cnl_library import CNLParser, pretty_json, human_readable_from_seconds
18
from split_text import split_proprtionally
19 20 21 22 23 24 25 26 27 28

## some "constants"/preferences
divisor = 1000000.0
rounding_digits = 2
unit = "MBits"


def format_timestamp(t):
    return time.strftime("%Y-%m-%d_%H:%M:%S", time.localtime(t))

29 30 31 32 33 34
def sprint_bold(text):
    return "\033[1m" + text + "\033[0m"

def sprint_inverted(text):
    return "\033[7m" + text + "\033[0m"

35
def print_inverted(text, **kwargs):
36 37 38 39 40 41 42
    #print( "\033[7m" + text + "\033[0m", **kwargs )
    print( sprint_inverted(text), **kwargs )


def print_in_two_columns(format_str, left_col, right_col):
    for l, r in zip_longest(left_col, right_col, fillvalue=""):
        print( format_str.format(l, r) )
43 44


45

Mario Hock's avatar
Mario Hock committed
46
def show_match(left_file, right_file, env=None):
Mario Hock's avatar
Mario Hock committed
47 48 49 50 51 52 53
    """
    Displays a brief summary of two CNL-file next to each other.

    Should be used with two files that belong to the same experiment
    (e.g. sender and receiver).
    """

Mario Hock's avatar
Mario Hock committed
54 55
    left_head, left_rates = left_file.as_column(env)
    right_head, right_rates = right_file.as_column(env)
Mario Hock's avatar
Mario Hock committed
56 57 58 59 60 61 62 63 64 65 66 67 68

    ## Head
    print_in_two_columns("{:<50} {}", left_head, right_head)

    ## Rates
    print_in_two_columns("{:<58} {}", left_rates, right_rates)
    #  Note: The escape sequence for the "rate-bar" is counted as characters.. :-/

    print()




69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
class LogAnalyzer:

    def __init__(self, cnl_file):
        self.cnl_file = cnl_file

        ## Get all fields to watch for activity (NIC, send and receive)
        self.nics = cnl_file.get_nics()
        self.watch_fields = list()
        nic_fields = [".send", ".receive"]
        for nic_name in self.nics:
            for nic_field in nic_fields:
                self.watch_fields.append( nic_name + nic_field )

        # important csv indices
        self.watch_indices = cnl_file.get_csv_indices_of(self.watch_fields)
        self.begin_index = cnl_file.get_csv_index_of("begin")
        self.end_index = cnl_file.get_csv_index_of("end")
Mario Hock's avatar
Mario Hock committed
86
        self.duration_index = cnl_file.get_csv_index_of("duration")
87 88 89 90 91 92 93 94 95


        ## Result variables
        self.experiment_start_time = None
        self.experiment_end_time = None
        self.recording_start_time = None
        self.recording_end_time = None

        self.sums = [0.0] * len(self.watch_fields)
Mario Hock's avatar
Mario Hock committed
96
        self.pause_time = 0
97

Mario Hock's avatar
Mario Hock committed
98 99 100
        ## Trigger "summarize"
        self._summarize()

101 102 103 104 105 106 107 108 109

    def _is_activity(self, line):
        for field_name in self.watch_indices:
            if ( float( line[field_name] ) > 0 ):
                return True

        return False

    def _sum_line(self, line):
Mario Hock's avatar
Mario Hock committed
110
        # Summarize the values of all watched fields (data send/received).
111
        for i in range( len(self.sums) ):
Mario Hock's avatar
Mario Hock committed
112
            self.sums[i] += line[ self.watch_indices[i] ] * line[self.duration_index]
113 114 115 116 117 118 119 120 121


    def _get_begin(self, line):
        return line[self.begin_index]

    def _get_end(self, line):
        return line[self.end_index]


Mario Hock's avatar
Mario Hock committed
122
    def _summarize(self):
123
        probably_pause_time = 0
124 125

        for line in self.cnl_file.get_csv_iterator():
126 127 128 129
            ## Sum watched columns.
            self._sum_line(line)

            ## Find experiment start and end time.
130 131 132 133 134 135 136 137 138
            if ( self._is_activity(line) ):

                # Find begin of the experiment.
                if ( not self.experiment_start_time ):
                    self.experiment_start_time = self._get_begin(line)

                # Find end of the experiment.
                self.experiment_end_time = self._get_end(line)

139 140 141 142 143 144 145 146 147
                # Since the experiment apparently has started and not finished yet,
                #   the last observed idle time was actually during the experiment.
                #   --> So, track it.
                self.pause_time += probably_pause_time
                probably_pause_time = 0

            ## Keep track of idle states during the experiment.
            else:
                probably_pause_time += line[self.duration_index]
148 149 150 151 152


        self.experiment_duration = self.experiment_end_time - self.experiment_start_time


Mario Hock's avatar
Mario Hock committed
153

154
    def show(self):
Mario Hock's avatar
Mario Hock committed
155 156 157 158 159 160 161
        print("=== Summary ===")
        #print( "Filename: " + os.path.relpath(self.cnl_file.filename) )
        form_str = "{:<10} {}"
        print( form_str.format("Filename:", os.path.basename(self.cnl_file.filename)) )
        print( form_str.format("Comment:", self.cnl_file.get_comment()) )
        print( form_str.format("Hostname:", self.cnl_file.get_sysinfo()["hostname"]) )
        print( form_str.format("Kernel:", self.cnl_file.get_sysinfo()["kernel"]) )
162 163
        print()

Mario Hock's avatar
Mario Hock committed
164 165 166
        print( form_str.format( "Start:", format_timestamp(self.experiment_start_time) ) )
        print( form_str.format( "End:", format_timestamp(self.experiment_end_time) ) )
        print( form_str.format( "Duration:", round(self.experiment_duration) ) )
167 168
        print()

Mario Hock's avatar
Mario Hock committed
169 170
        print( "CPUs: " + ", ".join(self.cnl_file.get_cpus()) )
        print( "NICs: " + ", ".join(self.cnl_file.get_nics()) )
171 172 173
        print()

        # Show average transmission rates.
Mario Hock's avatar
Mario Hock committed
174
        print("== Average transmission rates ==")
175 176
        for i in range( len(self.sums) ):
            speed = round(self.sums[i] / divisor / self.experiment_duration, rounding_digits)
Mario Hock's avatar
Mario Hock committed
177
            print( "{:<13} {:>10} {}/s".format(self.watch_fields[i]+":", speed, unit) )
178 179


Mario Hock's avatar
Mario Hock committed
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
    def show_brief(self):
        speeds = list()
        for i in range( len(self.sums) ):
            speed = "{:.2f}".format( round(self.sums[i] / divisor / self.experiment_duration, rounding_digits) )
            speeds.append( "{:>8} {}/s".format(speed, unit) )

        speeds_str = " ".join(speeds)
        filename = os.path.basename(self.cnl_file.filename)
        comments = self.cnl_file.get_comment().split(";")

        print( "{:<32} {}".format( filename + ":", speeds_str) )
        for comment in comments:
            print( "{:<32} {}".format( "", comment.strip()) )



Mario Hock's avatar
Mario Hock committed
196
    def as_column(self, env=None):
Mario Hock's avatar
Mario Hock committed
197 198 199 200 201 202
        ## TODO change name?

        ## Head

        head = list()

203 204 205
        filename = os.path.basename(self.cnl_file.filename)
        comments = self.cnl_file.get_comment().split(";")

206
        # file name
Mario Hock's avatar
Mario Hock committed
207
        head.append( filename )
208 209

        # comment(s)
210
        for comment in comments:
Mario Hock's avatar
Mario Hock committed
211
            head.append(comment.strip()[:48])
212

213
        # duration
Mario Hock's avatar
Mario Hock committed
214 215 216 217
        pause = ""
        if ( self.pause_time > 5 ):
            pause = "({} of that idle)".format( human_readable_from_seconds(self.pause_time) )
        head.append( "Duration: {} {}".format(human_readable_from_seconds(self.experiment_duration), pause) )
218 219

        # requested environment variables
Mario Hock's avatar
Mario Hock committed
220 221 222
        if ( env ):
            env_head = self.cnl_file.get_environment()
            for e in env:
223 224 225 226 227 228 229 230 231 232 233
                json = env_head.get(e)
                if ( json ):
                    text = pretty_json(json)
                    it = iter(text.split("\n"))

                    # first line
                    head.append( '{}: {}'.format(e, next(it)) )

                    # subsequent lines (if any)
                    for line in it:
                        head.append( line )
Mario Hock's avatar
Mario Hock committed
234 235


236

Mario Hock's avatar
Mario Hock committed
237 238 239
        ## Transmission rates

        rates = list()
240
        for i in range( len(self.sums) ):
Mario Hock's avatar
Mario Hock committed
241
            speed = self.sums[i] / (self.experiment_duration-self.pause_time)
242

243 244 245 246 247 248 249 250
            number_str = "{:.2f}".format( round(speed / divisor, rounding_digits) )
            bar_str = "{:<20}".format(number_str + " " + unit + "/s")

            label = "{:<6}".format( self.nics[int(i/2)] + ":" if i%2==0 else "" )

            parts = split_proprtionally(bar_str, [speed, 1000000*10000-speed])
            text = label + "|" + sprint_inverted(parts[0]) + parts[1] + "|"

Mario Hock's avatar
Mario Hock committed
251 252 253 254
            rates.append( text )

        return head, rates

255 256


Mario Hock's avatar
Mario Hock committed
257 258
    def visualize_brief(self, env=None):
        head, rates = self.as_column(env)
Mario Hock's avatar
Mario Hock committed
259 260

        print_in_two_columns("{:<50} {}", head, rates )
261 262


263 264 265 266 267

## MAIN ##
if __name__ == "__main__":
    import sys

Mario Hock's avatar
Mario Hock committed
268
    filenames = sorted( sys.argv[1:] )
269

Mario Hock's avatar
Mario Hock committed
270 271 272
    for filename in filenames:
        ## * Parse input file. *
        cnl_file = CNLParser(filename)
273

Mario Hock's avatar
Mario Hock committed
274
        log = LogAnalyzer(cnl_file)
Mario Hock's avatar
Mario Hock committed
275
        #log.summarize()
276

Mario Hock's avatar
Mario Hock committed
277
        if ( len(filenames) > 1 ):
278 279
            #log.show_brief()
            log.visualize_brief()
Mario Hock's avatar
Mario Hock committed
280 281 282
            print()
        else:
            log.show()