--- hatari/tools/debugger/hatari_profile.py 2019/04/09 08:53:43 1.1 +++ hatari/tools/debugger/hatari_profile.py 2019/04/09 08:58:32 1.1.1.2 @@ -2,10 +2,10 @@ # # Hatari profile data processor # -# 2013 (C) Eero Tamminen, licensed under GPL v2+ +# 2013-2017 (C) Eero Tamminen, licensed under GPL v2+ # """ -A tool for post-processing emulator HW profiling data. +A tool for post-processing Hatari emulator HW profiling data. In Hatari debugger you get (CPU) profiling data with the following commands (for Falcon DSP data, prefix commands with 'dsp'): @@ -18,7 +18,7 @@ Profiling information for code addresses assigned to (function) symbols to which those addresses belong to. All addresses between two symbol names (in profile file) or symbol addresses (read from symbols files) are assumed to belong to the -preceeding function/symbol. +preceding function/symbol. Tool output will contain at least: - (deduced) call counts, @@ -28,10 +28,10 @@ Tool output will contain at least: If profile data contains other information (e.g. cache misses), that is also shown. -Provided symbol information should be in same format as for Hatari -debugger 'symbols' command. Note that files containing absolute -addresses and ones containing relatives addresses need to be given -with different options! +Provided symbol information should be in the same format as for +the Hatari debugger 'symbols' command. Note that files containing +absolute addresses and ones containing relatives addresses need to +be given with different options! Usage: hatari-profile [options] @@ -80,16 +80,16 @@ For each given profile file, output is: When both -l and -f options are specified, they're combined. Produced -lists contain at least the number of items specified for -f option, -and more if there are additional items which percentage of the total -value is larger than one given for -l option. In callgraphs these -options mainly affect which nodes are highlighted. +lists contain at least the number of items specified for the -f option, +and more, if there are additional items which percentage of the total +value is larger than the one given for the -l option. In callgraphs +these options mainly affect which nodes are highlighted. If profile includes "caller" information, -p option can be used to see: - costs also for everything else that a subroutine calls, -- subroutine's "own" cost, which exclude costs for any further - subroutine calls that it does. +- subroutine's "own" cost, which excludes costs for any further + subroutine calls that it did (For more information on how these differ from normally shown costs, see Profiling section in Hatari manual.) @@ -99,15 +99,15 @@ Nodes with subroutine costs are shown as Call information filtering options: --no-calls <[bersux]+> remove calls of given types, default = 'rux' --ignore-to ignore calls to these symbols - --compact leave only single connection for symbols - that are directly connected + --compact leave only single connection between symbols + which are connected through single node path -(Give --no-calls option an unknown type to see type descriptions.) +(Give --no-calls option other char than [bersux] to see type descriptions.) is a comma separate list of symbol names, like this: --ignore-to _int_timerc,_int_vbl -These options change which calls are reported for functions and can +These options change which calls are reported for functions, and can affect the shape & complexity of the graph a lot. If you e.g. want to see just nodes with costs specific to -p option, use "--no-calls berux" option. @@ -117,8 +117,9 @@ handling switches [1], give handler name In callgraphs, one can then investigate them separately using "no-calls '' --only " options. -[1] Switching to interrupt handler gets recorded as "a call" to it - by the profiler, and such "calls" can happen at any time. +[1] CPU being interrupted by an interrupt handler gets recorded as + "a call" to that handler by the profiler, and because such "calls" + can happen at any time, then can mess graphs badly NOTE: costs shown with -p option include costs of exception handling code that interrupts the function calls. Normally effect of this @@ -153,8 +154,8 @@ if intermediate nodes have been removed Callgraph visualization options: - --mark mark nodes which names contain any - of the listed string(s) + --mark color nodes which names contain any + of the listed string(s) differently -e, --emph-limit percentage limit for highlighted nodes When -e limit is given, it is used for deciding which nodes to @@ -222,7 +223,7 @@ class Output: try: self.set_output(open(fname, "w")) return fname - except IOError, err: + except IOError as err: self.warning(err) return None @@ -315,7 +316,7 @@ class FunctionStats: # --------------------------------------------------------------------- class InstructionStats: "statistics on all instructions" - # not changable, these are expectatations about the data fields + # not changeable, these are expectatations about the data fields # in this, FunctionStats, ProfileCallers and ProfileGraph classes callcount_field = 0 instructions_field = 1 @@ -444,7 +445,7 @@ class ProfileSymbols(Output): return False oldname = symbols[addr] lendiff = abs(len(name) - len(oldname)) - minlen = min(len(name), min(oldname)) + minlen = min(len(name), len(oldname)) # don't warn about object name replacements, # or adding/removing short prefix or postfix if not (oldname.endswith('.o') or @@ -547,11 +548,11 @@ class ProfileSymbols(Output): return None def get_preceeding_symbol(self, addr): - "resolve non-function addresses to preceeding function name+offset" + "resolve non-function addresses to preceding function name+offset" # should be called only after profile addresses has started if self.symbols: if self.symbols_need_sort: - self.symbols_sorted = self.symbols.keys() + self.symbols_sorted = list(self.symbols.keys()) self.symbols_sorted.sort() self.symbols_need_sort = False idx = bisect_right(self.symbols_sorted, addr) - 1 @@ -1004,7 +1005,7 @@ class EmulatorProfile(Output): # continuation may skip to a function which name is not visible in profile file name, offset = self.symbols.get_preceeding_symbol(addr) symaddr = addr - offset - # if changed area, preceeding symbol can be before area start, + # if changed area, preceding symbol can be before area start, # so need to check both address, and name having changed if symaddr > function.addr and name != function.name: addr = symaddr @@ -1101,20 +1102,19 @@ class ProfileSorter: self.field = None self.show_subcosts = subcosts - def _cmp_field(self, i, j): - "compare currently selected field in profile data" - field = self.field - return cmp(self.profile[i].cost[field], self.profile[j].cost[field]) + def _cmp_field(self, i): + "return currently selected field in profile data" + return self.profile[i].cost[self.field] def get_combined_limit(self, field, count, limit): "return percentage for given profile field that satisfies both count & limit constraint" if not count: return limit - keys = self.profile.keys() + keys = list(self.profile.keys()) if len(keys) <= count: return 0.0 self.field = field - keys.sort(self._cmp_field, None, True) + keys.sort(key=self._cmp_field, reverse=True) total = self.stats.totals[field] function = self.profile[keys[count]] if self.show_subcosts and function.subtotal: @@ -1209,8 +1209,8 @@ class ProfileSorter: if self.stats.totals[field] == 0: return self.field = field - keys = self.profile.keys() - keys.sort(self._cmp_field, None, True) + keys = list(self.profile.keys()) + keys.sort(key=self._cmp_field, reverse=True) self._output_list(keys, count, limit, show_info) @@ -1462,10 +1462,10 @@ label="%s"; to_remove[addr] = True continue # refers just to itself? - if children == 1 and addr == function.child.keys()[0]: + if children == 1 and addr == tuple(function.child.keys())[0]: to_remove[addr] = True continue - if parents == 1 and addr == function.parent.keys()[0]: + if parents == 1 and addr == tuple(function.parent.keys())[0]: to_remove[addr] = True continue if self.remove_intermediate: @@ -1773,7 +1773,7 @@ class Main(Output): "open given path in given mode & return file object" try: return open(path, mode) - except IOError, err: + except IOError as err: self.usage("opening given '%s' file in mode '%s' failed:\n\t%s" % (path, mode, err)) def get_value(self, opt, arg, tofloat):