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):
Mario Hock's avatar
Mario Hock committed
123
        probably_pause_time = 0
124
125

        for line in self.cnl_file.get_csv_iterator():
Mario Hock's avatar
Mario Hock committed
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)

Mario Hock's avatar
Mario Hock committed
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()