From: Nicholas Nethercote Date: Wed, 12 Apr 2023 00:02:13 +0000 (+1000) Subject: Add diff and merge capability to `cg_annotate`. X-Git-Tag: VALGRIND_3_21_0~35 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1fdf0e728a047f0aab4de805576b6a3a84f37b79;p=thirdparty%2Fvalgrind.git Add diff and merge capability to `cg_annotate`. And deprecate the use of `cg_diff` and `cg_merge`. Because `cg_annotate` can do a better job, even annotating source files when doing diffs in some cases. The user requests merging by passing multiple cgout files to `cg_annotate`, and diffing by passing two cgout files to `cg_annotate` along with `--diff`. --- diff --git a/cachegrind/cg_annotate.in b/cachegrind/cg_annotate.in index 5e64a94485..c76a760be0 100755 --- a/cachegrind/cg_annotate.in +++ b/cachegrind/cg_annotate.in @@ -34,17 +34,28 @@ from __future__ import annotations +import filecmp import os import re import sys from argparse import ArgumentParser, BooleanOptionalAction, Namespace from collections import defaultdict -from typing import DefaultDict, NoReturn, TextIO +from typing import Callable, DefaultDict, NoReturn, TextIO +def die(msg: str) -> NoReturn: + print("cg_annotate: error:", msg, file=sys.stderr) + sys.exit(1) + + +SearchAndReplace = Callable[[str], str] + # A typed wrapper for parsed args. class Args(Namespace): # None of these fields are modified after arg parsing finishes. + diff: bool + mod_filename: SearchAndReplace + mod_funcname: SearchAndReplace show: list[str] sort: list[str] threshold: float # a percentage @@ -55,6 +66,42 @@ class Args(Namespace): @staticmethod def parse() -> Args: + # We support Perl-style `s/old/new/flags` search-and-replace + # expressions, because that's how this option was implemented in the + # old Perl version of `cg_diff`. This requires conversion from + # `s/old/new/` style to `re.sub`. The conversion isn't a perfect + # emulation of Perl regexps (e.g. Python uses `\1` rather than `$1` for + # using captures in the `new` part), but it should be close enough. The + # only supported flags are `g` (global) and `i` (ignore case). + def search_and_replace(regex: str | None) -> SearchAndReplace: + if regex is None: + return lambda s: s + + # Extract the parts of an `s/old/new/tail` regex. `(? list[str]: return values.split(",") @@ -97,9 +144,30 @@ class Args(Namespace): help=f"(deprecated) same as --no-{new_name}", ) - p = ArgumentParser(description="Process a Cachegrind output file.") + p = ArgumentParser(description="Process one or more Cachegrind output files.") p.add_argument("--version", action="version", version="%(prog)s-@VERSION@") + p.add_argument( + "--diff", + default=False, + action="store_true", + help="perform a diff between two Cachegrind output files", + ) + p.add_argument( + "--mod-filename", + type=search_and_replace, + metavar="REGEX", + default=search_and_replace(None), + help="a search-and-replace regex applied to filenames, e.g. " + "`s/prog[0-9]/progN/`", + ) + p.add_argument( + "--mod-funcname", + type=search_and_replace, + metavar="REGEX", + default=search_and_replace(None), + help="like --mod-filename, but for function names", + ) p.add_argument( "--show", type=comma_separated_list, @@ -143,12 +211,19 @@ class Args(Namespace): ) p.add_argument( "cgout_filename", - nargs=1, + nargs="+", metavar="cachegrind-out-file", help="file produced by Cachegrind", ) - return p.parse_args(namespace=Args()) + # `args0` name used to avoid shadowing the global `args`, which pylint + # doesn't like. + args0 = p.parse_args(namespace=Args()) + if args0.diff and len(args0.cgout_filename) != 2: + p.print_usage(file=sys.stderr) + die("argument --diff: requires exactly two Cachegrind output files") + + return args0 # Args are stored in a global for easy access. @@ -178,7 +253,11 @@ class Events: # Like `sort_events`, but indices into `events`, rather than names. sort_indices: list[int] - def __init__(self, text: str) -> None: + def __init__(self) -> None: + # All fields are left uninitialized here, and set instead in `init`. + pass + + def init(self, text: str) -> None: self.events = text.split() self.num_events = len(self.events) @@ -245,9 +324,15 @@ def add_cc_to_cc(a_cc: Cc, b_cc: Cc) -> None: b_cc[i] += a_count +# Subtract the counts in `a_cc` from `b_cc`. +def sub_cc_from_cc(a_cc: Cc, b_cc: Cc) -> None: + for i, a_count in enumerate(a_cc): + b_cc[i] -= a_count + + # Unrolled version of `add_cc_to_cc`, for speed. def add_cc_to_ccs( - a_cc: Cc, b_cc1: Cc, b_cc2: Cc, b_cc3: Cc, b_cc4: Cc, b_cc5: Cc + a_cc: Cc, b_cc1: Cc, b_cc2: Cc, b_cc3: Cc, b_cc4: Cc, b_cc5: Cc, total_cc: Cc ) -> None: for i, a_count in enumerate(a_cc): b_cc1[i] += a_count @@ -255,6 +340,21 @@ def add_cc_to_ccs( b_cc3[i] += a_count b_cc4[i] += a_count b_cc5[i] += a_count + total_cc[i] += a_count + + +# Unrolled version of `sub_cc_from_cc`, for speed. Note that the last one, +# `total_cc`, is added. +def sub_cc_from_ccs( + a_cc: Cc, b_cc1: Cc, b_cc2: Cc, b_cc3: Cc, b_cc4: Cc, b_cc5: Cc, total_cc: Cc +) -> None: + for i, a_count in enumerate(a_cc): + b_cc1[i] -= a_count + b_cc2[i] -= a_count + b_cc3[i] -= a_count + b_cc4[i] -= a_count + b_cc5[i] -= a_count + total_cc[i] += a_count # Update `min_cc` and `max_cc` with `self`. @@ -266,59 +366,70 @@ def update_cc_extremes(self: Cc, min_cc: Cc, max_cc: Cc) -> None: min_cc[i] = count -# A deep cost centre with a dict for the inner names and CCs. +# Note: some abbrevations used below: +# - Ofl/ofl: original filename, as mentioned in a cgout file. +# - Ofn/ofn: original function name, as mentioned in a cgout file. +# - Mfl/mfl: modified filename, the result of passing an Ofl through +# `--mod-filename`. +# - Mfn/mfn: modified function name, the result of passing an Ofn through +# `--mod-funcname`. +# - Mname/mname: modified name, used for what could be an Mfl or an Mfn. + +# A deep cost centre with a dict for the inner mnames and CCs. class Dcc: outer_cc: Cc - inner_dict_name_cc: DictNameCc + inner_dict_mname_cc: DictMnameCc - def __init__(self, outer_cc: Cc, inner_dict_name_cc: DictNameCc) -> None: + def __init__(self, outer_cc: Cc, inner_dict_mname_cc: DictMnameCc) -> None: self.outer_cc = outer_cc - self.inner_dict_name_cc = inner_dict_name_cc + self.inner_dict_mname_cc = inner_dict_mname_cc -# A deep cost centre with a list for the inner names and CCs. Used during +# A deep cost centre with a list for the inner mnames and CCs. Used during # filtering and sorting. class Lcc: outer_cc: Cc - inner_list_name_cc: ListNameCc + inner_list_mname_cc: ListMnameCc - def __init__(self, outer_cc: Cc, inner_list_name_cc: ListNameCc) -> None: + def __init__(self, outer_cc: Cc, inner_list_mname_cc: ListMnameCc) -> None: self.outer_cc = outer_cc - self.inner_list_name_cc = inner_list_name_cc + self.inner_list_mname_cc = inner_list_mname_cc -# Per-file/function CCs. The list version is used during filtering and sorting. -DictNameCc = DefaultDict[str, Cc] -ListNameCc = list[tuple[str, Cc]] +# Per-Mfl/Mfn CCs. The list version is used during filtering and sorting. +DictMnameCc = DefaultDict[str, Cc] +ListMnameCc = list[tuple[str, Cc]] -# Per-file/function DCCs. The outer names are filenames and the inner names are -# function names, or vice versa. The list version is used during filtering and -# sorting. -DictNameDcc = DefaultDict[str, Dcc] -ListNameLcc = list[tuple[str, Lcc]] +# Per-Mfl/Mfn DCCs. The outer Mnames are Mfls and the inner Mnames are Mfns, or +# vice versa. The list version is used during filtering and sorting. +DictMnameDcc = DefaultDict[str, Dcc] +ListMnameLcc = list[tuple[str, Lcc]] -# Per-line CCs, organised by filename and line number. +# Per-line CCs, organised by Mfl and line number. DictLineCc = DefaultDict[int, Cc] -DictFlDictLineCc = DefaultDict[str, DictLineCc] +DictMflDictLineCc = DefaultDict[str, DictLineCc] - -def die(msg: str) -> NoReturn: - print("cg_annotate: error:", msg, file=sys.stderr) - sys.exit(1) +# A dictionary tracking how Ofls get mapped to Mfls by `--mod-filename`. If +# `--mod-filename` isn't used, each entry will be the identity mapping: ("foo" +# -> set(["foo"])). +DictMflOfls = DefaultDict[str, set[str]] -def read_cgout_file() -> tuple[ - str, - str, - Events, - DictNameDcc, - DictNameDcc, - DictFlDictLineCc, - Cc, -]: +def read_cgout_file( + cgout_filename: str, + is_first_file: bool, + descs: list[str], + cmds: list[str], + events: Events, + dict_mfl_ofls: DictMflOfls, + dict_mfl_dcc: DictMnameDcc, + dict_mfn_dcc: DictMnameDcc, + dict_mfl_dict_line_cc: DictMflDictLineCc, + summary_cc: Cc, +) -> None: # The file format is described in Cachegrind's manual. try: - cgout_file = open(args.cgout_filename[0], "r", encoding="utf-8") + cgout_file = open(cgout_filename, "r", encoding="utf-8") except OSError as err: die(f"{err}") @@ -340,40 +451,64 @@ def read_cgout_file() -> tuple[ desc += m.group(1) + "\n" else: break + descs.append(desc) # Read "cmd:" line. (`line` is already set from the "desc:" loop.) if m := re.match(r"cmd:\s+(.*)", line): - cmd = m.group(1) + cmds.append(m.group(1)) else: parse_die("missing a `command:` line") # Read "events:" line. line = readline() if m := re.match(r"events:\s+(.*)", line): - events = Events(m.group(1)) + if is_first_file: + events.init(m.group(1)) + else: + events2 = Events() + events2.init(m.group(1)) + if events.events != events2.events: + die("events in data files don't match") else: parse_die("missing an `events:` line") def mk_empty_dict_line_cc() -> DictLineCc: return defaultdict(events.mk_empty_cc) - # The current filename and function name. - fl = "" - fn = "" - - # Different places where we accumulate CC data. - dict_fl_dcc: DictNameDcc = defaultdict(events.mk_empty_dcc) - dict_fn_dcc: DictNameDcc = defaultdict(events.mk_empty_dcc) - dict_fl_dict_line_cc: DictFlDictLineCc = defaultdict(mk_empty_dict_line_cc) - summary_cc = None + # The current Mfl and Mfn. + mfl = "" + mfn = "" + + # These values are passed in by reference and are modified by this + # function. But they can't be properly initialized until the `events:` + # line of the first file is read and the number of events is known. So + # we initialize them in an invalid state in `main`, and then + # reinitialize them properly here, before their first use. + if is_first_file: + dict_mfl_dcc.default_factory = events.mk_empty_dcc + dict_mfn_dcc.default_factory = events.mk_empty_dcc + dict_mfl_dict_line_cc.default_factory = mk_empty_dict_line_cc + summary_cc.extend(events.mk_empty_cc()) # These are refs into the dicts above, used to avoid repeated lookups. # They are all overwritten before first use. - fl_dcc = events.mk_empty_dcc() - fn_dcc = events.mk_empty_dcc() - fl_dcc_inner_fn_cc = events.mk_empty_cc() - fn_dcc_inner_fl_cc = events.mk_empty_cc() + mfl_dcc = events.mk_empty_dcc() + mfn_dcc = events.mk_empty_dcc() + mfl_dcc_inner_mfn_cc = events.mk_empty_cc() + mfn_dcc_inner_mfl_cc = events.mk_empty_cc() dict_line_cc = mk_empty_dict_line_cc() + total_cc = events.mk_empty_cc() + + # When diffing, we negate the first cgout file's counts to effectively + # achieve `cgout2 - cgout1`. + if args.diff and is_first_file: + combine_cc_with_cc = sub_cc_from_cc + combine_cc_with_ccs = sub_cc_from_ccs + else: + combine_cc_with_cc = add_cc_to_cc + combine_cc_with_ccs = add_cc_to_ccs + + summary_cc_present = False # Line matching is done in order of pattern frequency, for speed. while line := readline(): @@ -385,37 +520,54 @@ def read_cgout_file() -> tuple[ except ValueError: parse_die("malformed or too many event counts") - # Record this CC at the file:function level, the function:file - # level, and the file/line level. - add_cc_to_ccs( + # Record this CC at various levels. + combine_cc_with_ccs( cc, - fl_dcc.outer_cc, - fn_dcc.outer_cc, - fl_dcc_inner_fn_cc, - fn_dcc_inner_fl_cc, + mfl_dcc.outer_cc, + mfn_dcc.outer_cc, + mfl_dcc_inner_mfn_cc, + mfn_dcc_inner_mfl_cc, dict_line_cc[line_num], + total_cc, ) elif line.startswith("fn="): - fn = line[3:-1] - # `fl_dcc` is unchanged. - fn_dcc = dict_fn_dcc[fn] - fl_dcc_inner_fn_cc = fl_dcc.inner_dict_name_cc[fn] - fn_dcc_inner_fl_cc = fn_dcc.inner_dict_name_cc[fl] + ofn = line[3:-1] + mfn = args.mod_funcname(ofn) + # `mfl_dcc` is unchanged. + mfn_dcc = dict_mfn_dcc[mfn] + mfl_dcc_inner_mfn_cc = mfl_dcc.inner_dict_mname_cc[mfn] + mfn_dcc_inner_mfl_cc = mfn_dcc.inner_dict_mname_cc[mfl] elif line.startswith("fl="): - fl = line[3:-1] + ofl = line[3:-1] + mfl = args.mod_filename(ofl) + dict_mfl_ofls[mfl].add(ofl) # A `fn=` line should follow, overwriting the function name. - fn = "" - fl_dcc = dict_fl_dcc[fl] - fn_dcc = dict_fn_dcc[fn] - fl_dcc_inner_fn_cc = fl_dcc.inner_dict_name_cc[fn] - fn_dcc_inner_fl_cc = fn_dcc.inner_dict_name_cc[fl] - dict_line_cc = dict_fl_dict_line_cc[fl] + mfn = "" + mfl_dcc = dict_mfl_dcc[mfl] + mfn_dcc = dict_mfn_dcc[mfn] + mfl_dcc_inner_mfn_cc = mfl_dcc.inner_dict_mname_cc[mfn] + mfn_dcc_inner_mfl_cc = mfn_dcc.inner_dict_mname_cc[mfl] + dict_line_cc = dict_mfl_dict_line_cc[mfl] elif m := re.match(r"summary:\s+(.*)", line): + summary_cc_present = True try: - summary_cc = events.mk_cc(m.group(1).split()) + this_summary_cc = events.mk_cc(m.group(1).split()) + combine_cc_with_cc(this_summary_cc, summary_cc) + + # Check summary is correct. Note that `total_cc` doesn't + # get negated for the first file in a diff, unlike the + # other CCs, because it's only used here as a sanity check. + if this_summary_cc != total_cc: + msg = ( + "`summary:` line doesn't match computed total\n" + f"- summary: {this_summary_cc}\n" + f"- computed: {total_cc}" + ) + parse_die(msg) + except ValueError: parse_die("malformed or too many event counts") @@ -427,31 +579,9 @@ def read_cgout_file() -> tuple[ parse_die(f"malformed line: {line[:-1]}") # Check if summary line was present. - if not summary_cc: + if not summary_cc_present: parse_die("missing `summary:` line, aborting") - # Check summary is correct. (Only using the outer CCs.) - total_cc = events.mk_empty_cc() - for dcc in dict_fl_dcc.values(): - add_cc_to_cc(dcc.outer_cc, total_cc) - if summary_cc != total_cc: - msg = ( - "`summary:` line doesn't match computed total\n" - f"- summary: {summary_cc}\n" - f"- total: {total_cc}" - ) - parse_die(msg) - - return ( - desc, - cmd, - events, - dict_fl_dcc, - dict_fn_dcc, - dict_fl_dict_line_cc, - summary_cc, - ) - # The width of a column, in three parts. class Width: @@ -487,7 +617,7 @@ class CcPrinter: # Text of a missing CC, which can be computed in advance. missing_cc_str: str - # Must call `init_ccs` or `init_list_name_lcc` after this. + # Must call `init_ccs` or `init_list_mname_lcc` after this. def __init__(self, events: Events, summary_cc: Cc) -> None: self.events = events self.summary_cc = summary_cc @@ -505,7 +635,7 @@ class CcPrinter: self.init_widths(min_cc, max_cc, None, None) - def init_list_name_lcc(self, list_name_lcc: ListNameLcc) -> None: + def init_list_mname_lcc(self, list_mname_lcc: ListMnameLcc) -> None: self.events_prefix = " " cumul_cc = self.events.mk_empty_cc() @@ -516,10 +646,10 @@ class CcPrinter: max_cc = self.events.mk_empty_cc() min_cumul_cc = self.events.mk_empty_cc() max_cumul_cc = self.events.mk_empty_cc() - for _, lcc in list_name_lcc: + for _, lcc in list_mname_lcc: # Consider both outer and inner CCs for `count` and `perc1`. update_cc_extremes(lcc.outer_cc, min_cc, max_cc) - for _, inner_cc in lcc.inner_list_name_cc: + for _, inner_cc in lcc.inner_list_mname_cc: update_cc_extremes(inner_cc, min_cc, max_cc) # Consider only outer CCs for `perc2`. @@ -604,24 +734,24 @@ class CcPrinter: print(suffix) - def print_lcc(self, lcc: Lcc, outer_name: str, cumul_cc: Cc) -> None: - print("> ", end="") + def print_lcc(self, indent: str, lcc: Lcc, outer_mname: str, cumul_cc: Cc) -> None: + print(indent, end="") if ( - len(lcc.inner_list_name_cc) == 1 - and lcc.outer_cc == lcc.inner_list_name_cc[0][1] + len(lcc.inner_list_mname_cc) == 1 + and lcc.outer_cc == lcc.inner_list_mname_cc[0][1] ): # There is only one inner CC, it met the threshold, and it is equal # to the outer CC. Print the inner CC and outer CC in a single # line, because they are the same. - inner_name = lcc.inner_list_name_cc[0][0] - self.print_cc(lcc.outer_cc, cumul_cc, f"{outer_name}:{inner_name}") + inner_mname = lcc.inner_list_mname_cc[0][0] + self.print_cc(lcc.outer_cc, cumul_cc, f"{outer_mname}:{inner_mname}") else: # There are multiple inner CCs, and at least one met the threshold. # Print the outer CC and then the inner CCs, indented. - self.print_cc(lcc.outer_cc, cumul_cc, f"{outer_name}:") - for inner_name, inner_cc in lcc.inner_list_name_cc: + self.print_cc(lcc.outer_cc, cumul_cc, f"{outer_mname}:") + for inner_mname, inner_cc in lcc.inner_list_mname_cc: print(" ", end="") - self.print_cc(inner_cc, None, f" {inner_name}") + self.print_cc(inner_cc, None, f" {inner_mname}") print() # If `cc2` is `None`, it's a vanilla CC or inner CC. Otherwise, it's an @@ -645,15 +775,42 @@ def print_fancy(text: str) -> None: print(fancy) -def print_metadata(desc: str, cmd: str, events: Events) -> None: +def print_metadata(descs: list[str], cmds: list[str], events: Events) -> None: print_fancy("Metadata") - print(desc, end="") - print("Command: ", cmd) - print("Data file: ", args.cgout_filename[0]) + + def all_the_same(strs: list[str]) -> bool: + for i in range(len(strs) - 1): + if strs[i] != strs[i + 1]: + return False + + return True + + print("Invocation: ", *sys.argv) + + # When there are multiple descriptions, they are usually all the same. Only + # print the description once in that case. + if all_the_same(descs): + print(descs[0], end="") + else: + for i, desc in enumerate(descs): + print(f"Description {i+1}:") + print(desc, end="") + + # Commands are sometimes the same, sometimes not. Always print them + # individually, but refer to the previous one when appropriate. + if len(cmds) == 1: + print("Command: ", cmds[0]) + else: + for i, cmd in enumerate(cmds): + if i > 0 and cmds[i - 1] == cmd: + print(f"Command {i+1}: (same as Command {i})") + else: + print(f"Command {i+1}: ", cmd) + print("Events recorded: ", *events.events) print("Events shown: ", *events.show_events) print("Event sort order:", *events.sort_events) - print("Threshold: ", args.threshold) + print("Threshold: ", args.threshold, "%", sep="") print("Annotation: ", "on" if args.annotate else "off") print() @@ -668,8 +825,8 @@ def print_summary(events: Events, summary_cc: Cc) -> None: print() -def print_name_summary( - kind: str, events: Events, dict_name_dcc: DictNameDcc, summary_cc: Cc +def print_mname_summary( + kind: str, indent: str, events: Events, dict_mname_dcc: DictMnameDcc, summary_cc: Cc ) -> set[str]: # The primary sort event is used for the threshold. threshold_index = events.sort_indices[0] @@ -677,66 +834,67 @@ def print_name_summary( # Convert the threshold from a percentage to an event count. threshold = args.threshold * abs(summary_cc[threshold_index]) / 100 - def meets_threshold(name_and_cc: tuple[str, Cc]) -> bool: - cc = name_and_cc[1] + def meets_threshold(mname_and_cc: tuple[str, Cc]) -> bool: + cc = mname_and_cc[1] return abs(cc[threshold_index]) >= threshold # Create a list with the outer CC counts in sort order, so that # left-to-right list comparison does the right thing. Plus the outer name # at the end for deterministic output when all the event counts are # identical in two CCs. - def key_name_and_lcc(name_and_lcc: tuple[str, Lcc]) -> tuple[list[int], str]: - (outer_name, lcc) = name_and_lcc + def key_mname_and_lcc(mname_and_lcc: tuple[str, Lcc]) -> tuple[list[int], str]: + (outer_mname, lcc) = mname_and_lcc return ( [abs(lcc.outer_cc[i]) for i in events.sort_indices], - outer_name, + outer_mname, ) - # Similar to `key_name_and_lcc`. - def key_name_and_cc(name_and_cc: tuple[str, Cc]) -> tuple[list[int], str]: - (name, cc) = name_and_cc - return ([abs(cc[i]) for i in events.sort_indices], name) + # Similar to `key_mname_and_lcc`. + def key_mname_and_cc(mname_and_cc: tuple[str, Cc]) -> tuple[list[int], str]: + (mname, cc) = mname_and_cc + return ([abs(cc[i]) for i in events.sort_indices], mname) # This is a `filter_map` operation, which Python doesn't directly support. - list_name_lcc: ListNameLcc = [] - for outer_name, dcc in dict_name_dcc.items(): + list_mname_lcc: ListMnameLcc = [] + for outer_mname, dcc in dict_mname_dcc.items(): # Filter out inner CCs for which the primary sort event count is below the # threshold, and sort the remainder. - inner_list_name_cc = sorted( - filter(meets_threshold, dcc.inner_dict_name_cc.items()), - key=key_name_and_cc, + inner_list_mname_cc = sorted( + filter(meets_threshold, dcc.inner_dict_mname_cc.items()), + key=key_mname_and_cc, reverse=True, ) # If no inner CCs meet the threshold, ignore the entire DCC, even if # the outer CC meets the threshold. - if len(inner_list_name_cc) == 0: + if len(inner_list_mname_cc) == 0: continue - list_name_lcc.append((outer_name, Lcc(dcc.outer_cc, inner_list_name_cc))) + list_mname_lcc.append((outer_mname, Lcc(dcc.outer_cc, inner_list_mname_cc))) - list_name_lcc = sorted(list_name_lcc, key=key_name_and_lcc, reverse=True) + list_mname_lcc = sorted(list_mname_lcc, key=key_mname_and_lcc, reverse=True) printer = CcPrinter(events, summary_cc) - printer.init_list_name_lcc(list_name_lcc) + printer.init_list_mname_lcc(list_mname_lcc) print_fancy(kind + " summary") printer.print_events(" " + kind.lower()) print() # Print LCCs. - threshold_names = set([]) + threshold_mnames = set([]) cumul_cc = events.mk_empty_cc() - for name, lcc in list_name_lcc: + for mname, lcc in list_mname_lcc: add_cc_to_cc(lcc.outer_cc, cumul_cc) - printer.print_lcc(lcc, name, cumul_cc) - threshold_names.add(name) + printer.print_lcc(indent, lcc, mname, cumul_cc) + threshold_mnames.add(mname) - return threshold_names + return threshold_mnames class AnnotatedCcs: line_nums_known_cc: Cc line_nums_unknown_cc: Cc + non_identical_cc: Cc unreadable_cc: Cc below_threshold_cc: Cc files_unknown_cc: Cc @@ -744,6 +902,7 @@ class AnnotatedCcs: labels = [ " annotated: files known & above threshold & readable, line numbers known", " annotated: files known & above threshold & readable, line numbers unknown", + "unannotated: files known & above threshold & two or more non-identical", "unannotated: files known & above threshold & unreadable ", "unannotated: files known & below threshold", "unannotated: files unknown", @@ -752,6 +911,7 @@ class AnnotatedCcs: def __init__(self, events: Events) -> None: self.line_nums_known_cc = events.mk_empty_cc() self.line_nums_unknown_cc = events.mk_empty_cc() + self.non_identical_cc = events.mk_empty_cc() self.unreadable_cc = events.mk_empty_cc() self.below_threshold_cc = events.mk_empty_cc() self.files_unknown_cc = events.mk_empty_cc() @@ -760,6 +920,7 @@ class AnnotatedCcs: return [ self.line_nums_known_cc, self.line_nums_unknown_cc, + self.non_identical_cc, self.unreadable_cc, self.below_threshold_cc, self.files_unknown_cc, @@ -776,10 +937,11 @@ def mk_warning(msg: str) -> str: """ -def warn_src_file_is_newer(src_filename: str, cgout_filename: str) -> None: +def warn_ofls_are_all_newer(ofls: list[str], cgout_filename: str) -> None: + s = "".join([f"@ - {ofl}\n" for ofl in ofls]) msg = f"""\ -@ Source file '{src_filename}' is newer than data file '{cgout_filename}'. -@ Annotations may not be correct. +@ Original source files are all newer than data file '{cgout_filename}': +{s}@ Annotations may not be correct. """ print(mk_warning(msg)) @@ -798,10 +960,6 @@ def print_annotated_src_file( annotated_ccs: AnnotatedCcs, summary_cc: Cc, ) -> None: - # If the source file is more recent than the cgout file, issue warning. - if os.stat(src_file.name).st_mtime_ns > os.stat(args.cgout_filename[0]).st_mtime_ns: - warn_src_file_is_newer(src_file.name, args.cgout_filename[0]) - printer = CcPrinter(events, summary_cc) printer.init_ccs(list(dict_line_cc.values())) # The starting fancy has already been printed by the caller. @@ -884,52 +1042,101 @@ def print_annotated_src_file( print() -# This (partially) consumes `dict_fl_dict_line_cc`. +# This partially consumes `dict_mfl_dict_line_cc`, and fully consumes +# `dict_mfl_olfs`. def print_annotated_src_files( + ann_mfls: set[str], events: Events, - ann_src_filenames: set[str], - dict_fl_dict_line_cc: DictFlDictLineCc, + dict_mfl_ofls: DictMflOfls, + dict_mfl_dict_line_cc: DictMflDictLineCc, summary_cc: Cc, ) -> AnnotatedCcs: annotated_ccs = AnnotatedCcs(events) - def add_dict_line_cc_to_cc(dict_line_cc: DictLineCc | None, accum_cc: Cc) -> None: - if dict_line_cc: - for line_cc in dict_line_cc.values(): - add_cc_to_cc(line_cc, accum_cc) + def add_dict_line_cc_to_cc(dict_line_cc: DictLineCc, accum_cc: Cc) -> None: + for line_cc in dict_line_cc.values(): + add_cc_to_cc(line_cc, accum_cc) # Exclude the unknown ("???") file, which is unannotatable. - ann_src_filenames.discard("???") - dict_line_cc = dict_fl_dict_line_cc.pop("???", None) - add_dict_line_cc_to_cc(dict_line_cc, annotated_ccs.files_unknown_cc) + ann_mfls.discard("???") + if "???" in dict_mfl_dict_line_cc: + dict_line_cc = dict_mfl_dict_line_cc.pop("???") + add_dict_line_cc_to_cc(dict_line_cc, annotated_ccs.files_unknown_cc) + + def print_ann_fancy(mfl: str) -> None: + print_fancy(f"Annotated source file: {mfl}") + + # This can raise an `OSError`. + def all_ofl_contents_identical(ofls: list[str]) -> bool: + for i in range(len(ofls) - 1): + if not filecmp.cmp(ofls[i], ofls[i + 1], shallow=False): + return False + + return True - def print_ann_fancy(src_filename: str) -> None: - print_fancy(f"Annotated source file: {src_filename}") + for mfl in sorted(ann_mfls): + ofls = sorted(dict_mfl_ofls.pop(mfl)) + first_ofl = ofls[0] - for src_filename in sorted(ann_src_filenames): try: - with open(src_filename, "r", encoding="utf-8") as src_file: - dict_line_cc = dict_fl_dict_line_cc.pop(src_filename, None) - assert dict_line_cc is not None - print_ann_fancy(src_filename) - print_annotated_src_file( - events, - dict_line_cc, - src_file, - annotated_ccs, - summary_cc, + if all_ofl_contents_identical(ofls): + # All the Ofls that map to this Mfl are identical, which means we + # can annotate, and it doesn't matter which Ofl we use. + with open(first_ofl, "r", encoding="utf-8") as src_file: + dict_line_cc = dict_mfl_dict_line_cc.pop(mfl) + print_ann_fancy(mfl) + + # Because all the Ofls are identical, we can treat their + # mtimes as if they are all as early as the earliest one. + # Therefore, we warn only if the earliest source file is + # more recent than the cgout file. + min_ofl_st_mtime_ns = min( + [os.stat(ofl).st_mtime_ns for ofl in ofls] + ) + + for cgout_filename in args.cgout_filename: + if min_ofl_st_mtime_ns > os.stat(cgout_filename).st_mtime_ns: + warn_ofls_are_all_newer(ofls, cgout_filename) + + print_annotated_src_file( + events, + dict_line_cc, + src_file, + annotated_ccs, + summary_cc, + ) + else: + dict_line_cc = dict_mfl_dict_line_cc.pop(mfl) + add_dict_line_cc_to_cc(dict_line_cc, annotated_ccs.non_identical_cc) + + # We could potentially do better here. + # - Annotate until the first line where the src files diverge. + # - Also, heuristic resyncing, e.g. by looking for matching + # lines (of sufficient complexity) after a divergence. + print_ann_fancy(mfl) + print( + "Unannotated because two or more of these original files are not " + "identical:", + *ofls, + sep="\n- ", ) + print() except OSError: - dict_line_cc = dict_fl_dict_line_cc.pop(src_filename, None) + dict_line_cc = dict_mfl_dict_line_cc.pop(mfl) add_dict_line_cc_to_cc(dict_line_cc, annotated_ccs.unreadable_cc) - print_ann_fancy(src_filename) - print("This file was unreadable") + print_ann_fancy(mfl) + print( + "Unannotated because one or more of these original files are " + "unreadable:", + *ofls, + sep="\n- ", + ) print() - # Sum the CCs remaining in `dict_fl_dict_line_cc`, which are all in files + # Sum the CCs remaining in `dict_mfl_dict_line_cc`, which are all in files # below the threshold. - for dict_line_cc in dict_fl_dict_line_cc.values(): + for dict_line_cc in dict_mfl_dict_line_cc.values(): add_dict_line_cc_to_cc(dict_line_cc, annotated_ccs.below_threshold_cc) return annotated_ccs @@ -965,26 +1172,46 @@ def print_annotation_summary( def main() -> None: - ( - desc, - cmd, - events, - dict_fl_dcc, - dict_fn_dcc, - dict_fl_dict_line_cc, - summary_cc, - ) = read_cgout_file() + # Metadata, initialized to empty states. + descs: list[str] = [] + cmds: list[str] = [] + events = Events() + + # For tracking original filenames to modified filenames. + dict_mfl_ofls: DictMflOfls = defaultdict(set) + + # Different places where we accumulate CC data. Initialized to invalid + # states prior to the number of events being known. + dict_mfl_dcc: DictMnameDcc = defaultdict(None) + dict_mfn_dcc: DictMnameDcc = defaultdict(None) + dict_mfl_dict_line_cc: DictMflDictLineCc = defaultdict(None) + summary_cc: Cc = [] + + for n, filename in enumerate(args.cgout_filename): + is_first_file = n == 0 + read_cgout_file( + filename, + is_first_file, + descs, + cmds, + events, + dict_mfl_ofls, + dict_mfl_dcc, + dict_mfn_dcc, + dict_mfl_dict_line_cc, + summary_cc, + ) # Each of the following calls prints a section of the output. - print_metadata(desc, cmd, events) + print_metadata(descs, cmds, events) print_summary(events, summary_cc) - ann_src_filenames = print_name_summary( - "File:function", events, dict_fl_dcc, summary_cc + ann_mfls = print_mname_summary( + "File:function", "< ", events, dict_mfl_dcc, summary_cc ) - print_name_summary("Function:file", events, dict_fn_dcc, summary_cc) + print_mname_summary("Function:file", "> ", events, dict_mfn_dcc, summary_cc) if args.annotate: annotated_ccs = print_annotated_src_files( - events, ann_src_filenames, dict_fl_dict_line_cc, summary_cc + ann_mfls, events, dict_mfl_ofls, dict_mfl_dict_line_cc, summary_cc ) print_annotation_summary(events, annotated_ccs, summary_cc) diff --git a/cachegrind/cg_diff.in b/cachegrind/cg_diff.in index 38910f31b1..d3a63189ea 100755 --- a/cachegrind/cg_diff.in +++ b/cachegrind/cg_diff.in @@ -66,7 +66,7 @@ class Args(Namespace): if regex is None: return lambda s: s - # Extract the parts of a `s/old/new/tail` regex. `(? None: (cmd1, events1, dict_flfn_cc1, summary_cc1) = read_cgout_file(filename1) (cmd2, events2, dict_flfn_cc2, summary_cc2) = read_cgout_file(filename2) - if events1.num_events != events2.num_events: - die("events don't match") + if events1.events != events2.events: + die("events in data files don't match") # Subtract file 1's CCs from file 2's CCs, at the Flfn level. for flfn, flfn_cc1 in dict_flfn_cc1.items(): diff --git a/cachegrind/cg_merge.in b/cachegrind/cg_merge.in index 8304e8b279..7c385b4c8e 100755 --- a/cachegrind/cg_merge.in +++ b/cachegrind/cg_merge.in @@ -51,7 +51,11 @@ class Args(Namespace): @staticmethod def parse() -> Args: - p = ArgumentParser(description="Merge multiple Cachegrind output files.") + desc = ( + "Merge multiple Cachegrind output files. Deprecated; use " + "`cg_annotate` with multiple Cachegrind output files instead." + ) + p = ArgumentParser(description=desc) p.add_argument("--version", action="version", version="%(prog)s-@VERSION@") @@ -272,8 +276,8 @@ def main() -> None: events1 = events_n else: assert events1 - if events1.num_events != events_n.num_events: - die("events don't match") + if events1.events != events_n.events: + die("events in data files don't match") def write_output(f: TextIO) -> None: # These assertions hold because the loop above executes at least twice. diff --git a/cachegrind/tests/Makefile.am b/cachegrind/tests/Makefile.am index d38d300b90..9b977d5810 100644 --- a/cachegrind/tests/Makefile.am +++ b/cachegrind/tests/Makefile.am @@ -16,9 +16,17 @@ EXTRA_DIST = \ ann-diff1.post.exp ann-diff1.stderr.exp ann-diff1.vgtest \ ann-diff2.post.exp ann-diff2.stderr.exp ann-diff2.vgtest \ ann-diff2a.cgout ann-diff2b.cgout \ + ann-diff2-aux/ann-diff2-basic.rs \ + ann-diff3.post.exp ann-diff3.stderr.exp ann-diff3.vgtest \ + ann-diff4.post.exp ann-diff4.stderr.exp ann-diff4.vgtest \ + ann-diff4a.cgout ann-diff4b.cgout \ + ann-diff4a-aux/w.rs ann-diff4a-aux/x.rs ann-diff4a-aux/y.rs \ + ann-diff4a-aux/z.rs \ + ann-diff4b-aux/w.rs ann-diff4b-aux/x.rs ann-diff4b-aux/y.rs \ ann-merge1.post.exp ann-merge1.stderr.exp ann-merge1.vgtest \ ann-merge1a.cgout ann-merge1b.cgout \ ann-merge-x.rs ann-merge-y.rs \ + ann-merge2.post.exp ann-merge2.stderr.exp ann-merge2.vgtest \ ann1a.post.exp ann1a.stderr.exp ann1a.vgtest ann1.cgout \ ann1b.post.exp ann1b.stderr.exp ann1b.vgtest ann1b.cgout \ ann2.post.exp ann2.stderr.exp ann2.vgtest ann2.cgout \ diff --git a/cachegrind/tests/ann-diff1.post.exp b/cachegrind/tests/ann-diff1.post.exp index 54962b513d..d8ccea091e 100644 --- a/cachegrind/tests/ann-diff1.post.exp +++ b/cachegrind/tests/ann-diff1.post.exp @@ -1,13 +1,13 @@ -------------------------------------------------------------------------------- -- Metadata -------------------------------------------------------------------------------- +Invocation: ../cg_annotate --mod-filename=s/a.c/A.c/ --mod-funcname s/MAIN/Main/ ann-diff1.cgout Files compared: ann1.cgout; ann1b.cgout Command: ./a.out; ./a.out -Data file: ann-diff1.cgout Events recorded: Ir I1mr ILmr Dr D1mr DLmr Dw D1mw DLmw Events shown: Ir I1mr ILmr Dr D1mr DLmr Dw D1mw DLmw Event sort order: Ir I1mr ILmr Dr D1mr DLmr Dw D1mw DLmw -Threshold: 0.1 +Threshold: 0.1% Annotation: on -------------------------------------------------------------------------------- @@ -22,17 +22,17 @@ Ir________________ I1mr ILmr Dr_________________ D1mr DLmr Dw D1mw DLmw -------------------------------------------------------------------------------- Ir________________________ I1mr________ ILmr________ Dr_________________________ D1mr________ DLmr________ Dw__________ D1mw________ DLmw________ file:function -> 5,000,000 (100.0%, 100.0%) 0 (n/a, n/a) 0 (n/a, n/a) -2,000,000 (100.0%, 100.0%) 0 (n/a, n/a) 0 (n/a, n/a) 0 (n/a, n/a) 0 (n/a, n/a) 0 (n/a, n/a) a.c:MAIN +< 5,000,000 (100.0%, 100.0%) 0 (n/a, n/a) 0 (n/a, n/a) -2,000,000 (100.0%, 100.0%) 0 (n/a, n/a) 0 (n/a, n/a) 0 (n/a, n/a) 0 (n/a, n/a) 0 (n/a, n/a) A.c:Main -------------------------------------------------------------------------------- -- Function:file summary -------------------------------------------------------------------------------- Ir________________________ I1mr________ ILmr________ Dr_________________________ D1mr________ DLmr________ Dw__________ D1mw________ DLmw________ function:file -> 5,000,000 (100.0%, 100.0%) 0 (n/a, n/a) 0 (n/a, n/a) -2,000,000 (100.0%, 100.0%) 0 (n/a, n/a) 0 (n/a, n/a) 0 (n/a, n/a) 0 (n/a, n/a) 0 (n/a, n/a) MAIN:a.c +> 5,000,000 (100.0%, 100.0%) 0 (n/a, n/a) 0 (n/a, n/a) -2,000,000 (100.0%, 100.0%) 0 (n/a, n/a) 0 (n/a, n/a) 0 (n/a, n/a) 0 (n/a, n/a) 0 (n/a, n/a) Main:A.c -------------------------------------------------------------------------------- --- Annotated source file: a.c +-- Annotated source file: A.c -------------------------------------------------------------------------------- Ir________________ I1mr ILmr Dr_________________ D1mr DLmr Dw D1mw DLmw @@ -45,6 +45,7 @@ Ir________________ I1mr ILmr Dr_________________ D1mr DLmr Dw D1mw DLmw 0 0 0 0 0 0 0 0 0 annotated: files known & above threshold & readable, line numbers known 5,000,000 (100.0%) 0 0 -2,000,000 (100.0%) 0 0 0 0 0 annotated: files known & above threshold & readable, line numbers unknown + 0 0 0 0 0 0 0 0 0 unannotated: files known & above threshold & two or more non-identical 0 0 0 0 0 0 0 0 0 unannotated: files known & above threshold & unreadable 0 0 0 0 0 0 0 0 0 unannotated: files known & below threshold 0 0 0 0 0 0 0 0 0 unannotated: files unknown diff --git a/cachegrind/tests/ann-diff1.vgtest b/cachegrind/tests/ann-diff1.vgtest index e379401876..ab119b3b36 100644 --- a/cachegrind/tests/ann-diff1.vgtest +++ b/cachegrind/tests/ann-diff1.vgtest @@ -1,6 +1,8 @@ # The `prog` doesn't matter because we don't use its output. Instead we test -# the post-processing of the `ann{1,1b}.cgout` test files. +# the post-processing of the cgout files. prog: ../../tests/true vgopts: --cachegrind-out-file=cachegrind.out -post: python3 ../cg_diff --mod-funcname="s/main/MAIN/" ann1.cgout ann1b.cgout > ann-diff1.cgout && python3 ../cg_annotate ann-diff1.cgout -cleanup: rm ann-diff1.cgout + +post: python3 ../cg_diff --mod-funcname="s/main/MAIN/" ann1.cgout ann1b.cgout > ann-diff1.cgout && python3 ../cg_annotate --mod-filename="s/a.c/A.c/" --mod-funcname s/MAIN/Main/ ann-diff1.cgout + +cleanup: rm cachegrind.out ann-diff1.cgout diff --git a/cachegrind/tests/ann-diff2.post.exp b/cachegrind/tests/ann-diff2.post.exp index e1060dbd23..b6567418f7 100644 --- a/cachegrind/tests/ann-diff2.post.exp +++ b/cachegrind/tests/ann-diff2.post.exp @@ -1,13 +1,13 @@ -------------------------------------------------------------------------------- -- Metadata -------------------------------------------------------------------------------- +Invocation: ../cg_annotate ann-diff2c.cgout Files compared: ann-diff2a.cgout; ann-diff2b.cgout Command: cmd1; cmd2 -Data file: ann-diff2c.cgout Events recorded: One Two Events shown: One Two Event sort order: One Two -Threshold: 0.1 +Threshold: 0.1% Annotation: on -------------------------------------------------------------------------------- @@ -22,7 +22,7 @@ One___________ Two___________ -------------------------------------------------------------------------------- One___________________ Two___________________ file:function -> 2,100 (100.0%, 100.0%) 1,900 (100.0%, 100.0%) aux/ann-diff2-basic.rs: +< 2,100 (100.0%, 100.0%) 1,900 (100.0%, 100.0%) aux/ann-diff2-basic.rs: 1,000 (47.6%) 1,000 (52.6%) groffN 1,000 (47.6%) 1,000 (52.6%) fN_ffN_fooN_F4_g5 100 (4.8%) -100 (-5.3%) basic1 @@ -41,7 +41,8 @@ One___________ Two___________ -------------------------------------------------------------------------------- -- Annotated source file: aux/ann-diff2-basic.rs -------------------------------------------------------------------------------- -This file was unreadable +Unannotated because one or more of these original files are unreadable: +- aux/ann-diff2-basic.rs -------------------------------------------------------------------------------- -- Annotation summary @@ -50,6 +51,7 @@ One___________ Two___________ 0 0 annotated: files known & above threshold & readable, line numbers known 0 0 annotated: files known & above threshold & readable, line numbers unknown + 0 0 unannotated: files known & above threshold & two or more non-identical 2,100 (100.0%) 1,900 (100.0%) unannotated: files known & above threshold & unreadable 0 0 unannotated: files known & below threshold 0 0 unannotated: files unknown diff --git a/cachegrind/tests/ann-diff2.vgtest b/cachegrind/tests/ann-diff2.vgtest index 7b395e4e48..bae3ab9875 100644 --- a/cachegrind/tests/ann-diff2.vgtest +++ b/cachegrind/tests/ann-diff2.vgtest @@ -1,6 +1,8 @@ # The `prog` doesn't matter because we don't use its output. Instead we test -# the post-processing of the `ann-diff2{a,b}.cgout` test files. +# the post-processing of the cgout files. prog: ../../tests/true vgopts: --cachegrind-out-file=cachegrind.out + post: python3 ../cg_diff --mod-filename="s/.*aux\//aux\//i" --mod-funcname="s/(f[a-z]*)[0-9]/\1N/g" ann-diff2a.cgout ann-diff2b.cgout > ann-diff2c.cgout && python3 ../cg_annotate ann-diff2c.cgout -cleanup: rm ann-diff2c.cgout + +cleanup: rm cachegrind.out ann-diff2c.cgout diff --git a/cachegrind/tests/ann-diff2b.cgout b/cachegrind/tests/ann-diff2b.cgout index 9fb733e708..e6864107bb 100644 --- a/cachegrind/tests/ann-diff2b.cgout +++ b/cachegrind/tests/ann-diff2b.cgout @@ -1,4 +1,4 @@ -desc: Description for ann-diff2a.cgout +desc: Description for ann-diff2b.cgout cmd: cmd2 events: One Two diff --git a/cachegrind/tests/ann-diff3.post.exp b/cachegrind/tests/ann-diff3.post.exp new file mode 100644 index 0000000000..fa7ea4ad7b --- /dev/null +++ b/cachegrind/tests/ann-diff3.post.exp @@ -0,0 +1,63 @@ +-------------------------------------------------------------------------------- +-- Metadata +-------------------------------------------------------------------------------- +Invocation: ../cg_annotate --diff --mod-filename=s/.*aux\//aux\//i --mod-funcname=s/(f[a-z]*)[0-9]/\1N/g ann-diff2a.cgout ann-diff2b.cgout +Description 1: +Description for ann-diff2a.cgout +Description 2: +Description for ann-diff2b.cgout +Command 1: cmd1 +Command 2: cmd2 +Events recorded: One Two +Events shown: One Two +Event sort order: One Two +Threshold: 0.1% +Annotation: on + +-------------------------------------------------------------------------------- +-- Summary +-------------------------------------------------------------------------------- +One___________ Two___________ + +2,100 (100.0%) 1,900 (100.0%) PROGRAM TOTALS + +-------------------------------------------------------------------------------- +-- File:function summary +-------------------------------------------------------------------------------- + One___________________ Two___________________ file:function + +< 2,100 (100.0%, 100.0%) 1,900 (100.0%, 100.0%) aux/ann-diff2-basic.rs: + 1,000 (47.6%) 1,000 (52.6%) groffN + 1,000 (47.6%) 1,000 (52.6%) fN_ffN_fooN_F4_g5 + 100 (4.8%) -100 (-5.3%) basic1 + +-------------------------------------------------------------------------------- +-- Function:file summary +-------------------------------------------------------------------------------- + One__________________ Two__________________ function:file + +> 1,000 (47.6%, 47.6%) 1,000 (52.6%, 52.6%) groffN:aux/ann-diff2-basic.rs + +> 1,000 (47.6%, 95.2%) 1,000 (52.6%, 105.3%) fN_ffN_fooN_F4_g5:aux/ann-diff2-basic.rs + +> 100 (4.8%, 100.0%) -100 (-5.3%, 100.0%) basic1:aux/ann-diff2-basic.rs + +-------------------------------------------------------------------------------- +-- Annotated source file: aux/ann-diff2-basic.rs +-------------------------------------------------------------------------------- +Unannotated because one or more of these original files are unreadable: +- ann2-diff-AUX/ann-diff2-basic.rs +- ann2-diff-Aux/ann-diff2-basic.rs + +-------------------------------------------------------------------------------- +-- Annotation summary +-------------------------------------------------------------------------------- +One___________ Two___________ + + 0 0 annotated: files known & above threshold & readable, line numbers known + 0 0 annotated: files known & above threshold & readable, line numbers unknown + 0 0 unannotated: files known & above threshold & two or more non-identical +2,100 (100.0%) 1,900 (100.0%) unannotated: files known & above threshold & unreadable + 0 0 unannotated: files known & below threshold + 0 0 unannotated: files unknown + diff --git a/cachegrind/tests/ann-diff3.stderr.exp b/cachegrind/tests/ann-diff3.stderr.exp new file mode 100644 index 0000000000..ec68407b27 --- /dev/null +++ b/cachegrind/tests/ann-diff3.stderr.exp @@ -0,0 +1,3 @@ + + +I refs: diff --git a/cachegrind/tests/ann-diff3.vgtest b/cachegrind/tests/ann-diff3.vgtest new file mode 100644 index 0000000000..5831e3de61 --- /dev/null +++ b/cachegrind/tests/ann-diff3.vgtest @@ -0,0 +1,8 @@ +# The `prog` doesn't matter because we don't use its output. Instead we test +# the post-processing of the cgout files. +prog: ../../tests/true +vgopts: --cachegrind-out-file=cachegrind.out + +post: python3 ../cg_annotate --diff --mod-filename="s/.*aux\//aux\//i" --mod-funcname="s/(f[a-z]*)[0-9]/\1N/g" ann-diff2a.cgout ann-diff2b.cgout + +cleanup: rm cachegrind.out diff --git a/cachegrind/tests/ann-diff4.post.exp b/cachegrind/tests/ann-diff4.post.exp new file mode 100644 index 0000000000..0196948a62 --- /dev/null +++ b/cachegrind/tests/ann-diff4.post.exp @@ -0,0 +1,125 @@ +-------------------------------------------------------------------------------- +-- Metadata +-------------------------------------------------------------------------------- +Invocation: ../cg_annotate ann-diff4a.cgout ann-diff4b.cgout --mod-filename=s/ann-diff4[ab]/ann-diff4N/ --diff +DescA +DescB +DescC +Command 1: my-command +Command 2: (same as Command 1) +Events recorded: Ir +Events shown: Ir +Event sort order: Ir +Threshold: 0.1% +Annotation: on + +-------------------------------------------------------------------------------- +-- Summary +-------------------------------------------------------------------------------- +Ir__________ + +700 (100.0%) PROGRAM TOTALS + +-------------------------------------------------------------------------------- +-- File:function summary +-------------------------------------------------------------------------------- + Ir___________________ file:function + +< 600 (85.7%, 85.7%) ann-diff4N-aux/y.rs:b + +< 200 (28.6%, 114.3%) ann-diff4N-aux/x.rs:a + +< -200 (-28.6%, 85.7%) ann-diff4N-aux/w.rs:a + +< 200 (28.6%, 114.3%) ann-diff4N-aux/no-such-file.rs:f + +< -100 (-14.3%, 100.0%) ann-diff4N-aux/z.rs:c + +-------------------------------------------------------------------------------- +-- Function:file summary +-------------------------------------------------------------------------------- + Ir___________________ function:file + +> 600 (85.7%, 85.7%) b:ann-diff4N-aux/y.rs + +> 200 (28.6%, 114.3%) f:ann-diff4N-aux/no-such-file.rs + +> -100 (-14.3%, 100.0%) c:ann-diff4N-aux/z.rs + +> 0 (0.0%, 100.0%) a: + 200 (28.6%) ann-diff4N-aux/x.rs + -200 (-28.6%) ann-diff4N-aux/w.rs + +-------------------------------------------------------------------------------- +-- Annotated source file: ann-diff4N-aux/no-such-file.rs +-------------------------------------------------------------------------------- +Unannotated because one or more of these original files are unreadable: +- ann-diff4a-aux/no-such-file.rs +- ann-diff4b-aux/no-such-file.rs + +-------------------------------------------------------------------------------- +-- Annotated source file: ann-diff4N-aux/w.rs +-------------------------------------------------------------------------------- +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@ WARNING @@ WARNING @@ WARNING @@ WARNING @@ WARNING @@ WARNING @@ WARNING @@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Original source files are all newer than data file 'ann-diff4a.cgout': +@ - ann-diff4a-aux/w.rs +@ - ann-diff4b-aux/w.rs +@ Annotations may not be correct. +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@@ WARNING @@ WARNING @@ WARNING @@ WARNING @@ WARNING @@ WARNING @@ WARNING @@ +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Original source files are all newer than data file 'ann-diff4b.cgout': +@ - ann-diff4a-aux/w.rs +@ - ann-diff4b-aux/w.rs +@ Annotations may not be correct. +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + +Ir___________ + +-200 (-28.6%) one + . two + . three + +-------------------------------------------------------------------------------- +-- Annotated source file: ann-diff4N-aux/x.rs +-------------------------------------------------------------------------------- +Ir___________ + + 100 (14.3%) + +-200 (-28.6%) one + 300 (42.9%) two + . three + . four + . five + +-------------------------------------------------------------------------------- +-- Annotated source file: ann-diff4N-aux/y.rs +-------------------------------------------------------------------------------- +Unannotated because two or more of these original files are not identical: +- ann-diff4a-aux/y.rs +- ann-diff4b-aux/y.rs + +-------------------------------------------------------------------------------- +-- Annotated source file: ann-diff4N-aux/z.rs +-------------------------------------------------------------------------------- +Unannotated because one or more of these original files are unreadable: +- ann-diff4a-aux/z.rs +- ann-diff4b-aux/z.rs + +-------------------------------------------------------------------------------- +-- Annotation summary +-------------------------------------------------------------------------------- +Ir___________ + +-100 (-14.3%) annotated: files known & above threshold & readable, line numbers known + 100 (14.3%) annotated: files known & above threshold & readable, line numbers unknown + 600 (85.7%) unannotated: files known & above threshold & two or more non-identical + 100 (14.3%) unannotated: files known & above threshold & unreadable + 0 unannotated: files known & below threshold + 0 unannotated: files unknown + diff --git a/cachegrind/tests/ann-diff4.stderr.exp b/cachegrind/tests/ann-diff4.stderr.exp new file mode 100644 index 0000000000..ec68407b27 --- /dev/null +++ b/cachegrind/tests/ann-diff4.stderr.exp @@ -0,0 +1,3 @@ + + +I refs: diff --git a/cachegrind/tests/ann-diff4.vgtest b/cachegrind/tests/ann-diff4.vgtest new file mode 100644 index 0000000000..da6e00a216 --- /dev/null +++ b/cachegrind/tests/ann-diff4.vgtest @@ -0,0 +1,14 @@ +# The `prog` doesn't matter because we don't use its output. Instead we test +# the post-processing of the cgout files. +prog: ../../tests/true +vgopts: --cachegrind-out-file=cachegrind.out + +# The `sleep` is to ensure the mtime of the second touched file is greater than +# the mtime of the first touched file. +# +# Because only one of the two x.rs files is newer than the cgout files, we don't +# get an mtime warning on x.rs. But both w.rs files are new than the cgout +# files, so we do get an mtime warning on w.rs. +post: touch ann-diff4a.cgout ann-diff4b.cgout && sleep 0.1 && touch ann-diff4a-aux/x.rs ann-diff4a-aux/w.rs ann-diff4b-aux/w.rs && python3 ../cg_annotate ann-diff4a.cgout ann-diff4b.cgout --mod-filename=s/ann-diff4[ab]/ann-diff4N/ --diff + +cleanup: rm cachegrind.out diff --git a/cachegrind/tests/ann-diff4a-aux/w.rs b/cachegrind/tests/ann-diff4a-aux/w.rs new file mode 100644 index 0000000000..4cb29ea38f --- /dev/null +++ b/cachegrind/tests/ann-diff4a-aux/w.rs @@ -0,0 +1,3 @@ +one +two +three diff --git a/cachegrind/tests/ann-diff4a-aux/x.rs b/cachegrind/tests/ann-diff4a-aux/x.rs new file mode 100644 index 0000000000..b2f931a673 --- /dev/null +++ b/cachegrind/tests/ann-diff4a-aux/x.rs @@ -0,0 +1,5 @@ +one +two +three +four +five diff --git a/cachegrind/tests/ann-diff4a-aux/y.rs b/cachegrind/tests/ann-diff4a-aux/y.rs new file mode 100644 index 0000000000..f196eb52c6 --- /dev/null +++ b/cachegrind/tests/ann-diff4a-aux/y.rs @@ -0,0 +1,5 @@ +ONE +TWO +THREE +FOUR +FIVE diff --git a/cachegrind/tests/ann-diff4a-aux/z.rs b/cachegrind/tests/ann-diff4a-aux/z.rs new file mode 100644 index 0000000000..4cb29ea38f --- /dev/null +++ b/cachegrind/tests/ann-diff4a-aux/z.rs @@ -0,0 +1,3 @@ +one +two +three diff --git a/cachegrind/tests/ann-diff4a.cgout b/cachegrind/tests/ann-diff4a.cgout new file mode 100644 index 0000000000..a424c0a3b6 --- /dev/null +++ b/cachegrind/tests/ann-diff4a.cgout @@ -0,0 +1,30 @@ +desc: DescA +desc: DescB +desc: DescC +cmd: my-command +events: Ir + +fl=ann-diff4a-aux/w.rs +fn=a +1 1000 + +fl=ann-diff4a-aux/x.rs +fn=a +0 1000 +1 1000 +2 1000 + +fl=ann-diff4a-aux/y.rs +fn=b +4 1000 +5 1000 + +fl=ann-diff4a-aux/z.rs +fn=c +1 1000 + +fl=ann-diff4a-aux/no-such-file.rs +fn=f +0 100 + +summary: 7100 diff --git a/cachegrind/tests/ann-diff4b-aux/x.rs b/cachegrind/tests/ann-diff4b-aux/x.rs new file mode 100644 index 0000000000..b2f931a673 --- /dev/null +++ b/cachegrind/tests/ann-diff4b-aux/x.rs @@ -0,0 +1,5 @@ +one +two +three +four +five diff --git a/cachegrind/tests/ann-diff4b-aux/y.rs b/cachegrind/tests/ann-diff4b-aux/y.rs new file mode 100644 index 0000000000..cbb67cf3b4 --- /dev/null +++ b/cachegrind/tests/ann-diff4b-aux/y.rs @@ -0,0 +1,6 @@ +ONE +TWO +THREE +FOUR +FIVE +SIX diff --git a/cachegrind/tests/ann-diff4b.cgout b/cachegrind/tests/ann-diff4b.cgout new file mode 100644 index 0000000000..6f0c92d1f6 --- /dev/null +++ b/cachegrind/tests/ann-diff4b.cgout @@ -0,0 +1,31 @@ +desc: DescA +desc: DescB +desc: DescC +cmd: my-command +events: Ir + +fl=ann-diff4b-aux/w.rs +fn=a +1 800 + +fl=ann-diff4b-aux/x.rs +fn=a +0 1100 +1 800 +2 1300 + +fl=ann-diff4b-aux/y.rs +fn=b +4 1200 +5 900 +6 500 + +fl=ann-diff4b-aux/z.rs +fn=c +1 900 + +fl=ann-diff4b-aux/no-such-file.rs +fn=f +0 300 + +summary: 7800 diff --git a/cachegrind/tests/ann-merge1.post.exp b/cachegrind/tests/ann-merge1.post.exp index 1f47332b8a..ddb4931d80 100644 --- a/cachegrind/tests/ann-merge1.post.exp +++ b/cachegrind/tests/ann-merge1.post.exp @@ -1,14 +1,14 @@ -------------------------------------------------------------------------------- -- Metadata -------------------------------------------------------------------------------- +Invocation: ../cg_annotate ann-merge1c.cgout Description 1a Description 1b Command: Command 1 -Data file: ann-merge1c.cgout Events recorded: A Events shown: A Event sort order: A -Threshold: 0.1 +Threshold: 0.1% Annotation: on -------------------------------------------------------------------------------- @@ -23,12 +23,12 @@ A__________ -------------------------------------------------------------------------------- A_________________ file:function -> 70 (81.4%, 81.4%) ann-merge-x.rs: +< 70 (81.4%, 81.4%) ann-merge-x.rs: 40 (46.5%) x1 20 (23.3%) x3 10 (11.6%) x2 -> 16 (18.6%, 100.0%) ann-merge-y.rs:y1 +< 16 (18.6%, 100.0%) ann-merge-y.rs:y1 -------------------------------------------------------------------------------- -- Function:file summary @@ -73,6 +73,7 @@ A__________ 86 (100.0%) annotated: files known & above threshold & readable, line numbers known 0 annotated: files known & above threshold & readable, line numbers unknown + 0 unannotated: files known & above threshold & two or more non-identical 0 unannotated: files known & above threshold & unreadable 0 unannotated: files known & below threshold 0 unannotated: files unknown diff --git a/cachegrind/tests/ann-merge1.vgtest b/cachegrind/tests/ann-merge1.vgtest index b0b0eedc96..2779de1d12 100644 --- a/cachegrind/tests/ann-merge1.vgtest +++ b/cachegrind/tests/ann-merge1.vgtest @@ -1,7 +1,8 @@ # The `prog` doesn't matter because we don't use its output. Instead we test -# the post-processing of the `ann{1,1b}.cgout` test files. +# the post-processing of the cgout files. prog: ../../tests/true vgopts: --cachegrind-out-file=cachegrind.out + post: python3 ../cg_merge ann-merge1a.cgout ann-merge1b.cgout > ann-merge1c.cgout && python3 ../cg_annotate ann-merge1c.cgout -cleanup: rm ann-merge1c.cgout +cleanup: rm cachegrind.out ann-merge1c.cgout diff --git a/cachegrind/tests/ann-merge2.post.exp b/cachegrind/tests/ann-merge2.post.exp new file mode 100644 index 0000000000..bececb9166 --- /dev/null +++ b/cachegrind/tests/ann-merge2.post.exp @@ -0,0 +1,85 @@ +-------------------------------------------------------------------------------- +-- Metadata +-------------------------------------------------------------------------------- +Invocation: ../cg_annotate ann-merge1a.cgout ann-merge1b.cgout +Description 1: +Description 1a +Description 1b +Description 2: +Description 2a +Description 2b +Command 1: Command 1 +Command 2: Command 2 +Events recorded: A +Events shown: A +Event sort order: A +Threshold: 0.1% +Annotation: on + +-------------------------------------------------------------------------------- +-- Summary +-------------------------------------------------------------------------------- +A__________ + +86 (100.0%) PROGRAM TOTALS + +-------------------------------------------------------------------------------- +-- File:function summary +-------------------------------------------------------------------------------- + A_________________ file:function + +< 70 (81.4%, 81.4%) ann-merge-x.rs: + 40 (46.5%) x1 + 20 (23.3%) x3 + 10 (11.6%) x2 + +< 16 (18.6%, 100.0%) ann-merge-y.rs:y1 + +-------------------------------------------------------------------------------- +-- Function:file summary +-------------------------------------------------------------------------------- + A_________________ function:file + +> 40 (46.5%, 46.5%) x1:ann-merge-x.rs + +> 20 (23.3%, 69.8%) x3:ann-merge-x.rs + +> 16 (18.6%, 88.4%) y1:ann-merge-y.rs + +> 10 (11.6%, 100.0%) x2:ann-merge-x.rs + +-------------------------------------------------------------------------------- +-- Annotated source file: ann-merge-x.rs +-------------------------------------------------------------------------------- +A_________ + +20 (23.3%) one +10 (11.6%) two +10 (11.6%) three +10 (11.6%) four +20 (23.3%) five + +-------------------------------------------------------------------------------- +-- Annotated source file: ann-merge-y.rs +-------------------------------------------------------------------------------- +A_______ + +8 (9.3%) one +8 (9.3%) two +. three +. four +. five +. six + +-------------------------------------------------------------------------------- +-- Annotation summary +-------------------------------------------------------------------------------- +A__________ + +86 (100.0%) annotated: files known & above threshold & readable, line numbers known + 0 annotated: files known & above threshold & readable, line numbers unknown + 0 unannotated: files known & above threshold & two or more non-identical + 0 unannotated: files known & above threshold & unreadable + 0 unannotated: files known & below threshold + 0 unannotated: files unknown + diff --git a/cachegrind/tests/ann-merge2.stderr.exp b/cachegrind/tests/ann-merge2.stderr.exp new file mode 100644 index 0000000000..ec68407b27 --- /dev/null +++ b/cachegrind/tests/ann-merge2.stderr.exp @@ -0,0 +1,3 @@ + + +I refs: diff --git a/cachegrind/tests/ann-merge2.vgtest b/cachegrind/tests/ann-merge2.vgtest new file mode 100644 index 0000000000..adbdcde2c4 --- /dev/null +++ b/cachegrind/tests/ann-merge2.vgtest @@ -0,0 +1,8 @@ +# The `prog` doesn't matter because we don't use its output. Instead we test +# the post-processing of the cgout files. +prog: ../../tests/true +vgopts: --cachegrind-out-file=cachegrind.out + +post: python3 ../cg_annotate ann-merge1a.cgout ann-merge1b.cgout + +cleanup: rm cachegrind.out diff --git a/cachegrind/tests/ann1a.post.exp b/cachegrind/tests/ann1a.post.exp index bde53e6501..ed6e670481 100644 --- a/cachegrind/tests/ann1a.post.exp +++ b/cachegrind/tests/ann1a.post.exp @@ -1,15 +1,15 @@ -------------------------------------------------------------------------------- -- Metadata -------------------------------------------------------------------------------- +Invocation: ../cg_annotate --show=Ir,I1mr,ILmr --show-percs=no ann1.cgout I1 cache: 32768 B, 64 B, 8-way associative D1 cache: 32768 B, 64 B, 8-way associative LL cache: 19922944 B, 64 B, 19-way associative Command: ./a.out -Data file: ann1.cgout Events recorded: Ir I1mr ILmr Dr D1mr DLmr Dw D1mw DLmw Events shown: Ir I1mr ILmr Event sort order: Ir I1mr ILmr Dr D1mr DLmr Dw D1mw DLmw -Threshold: 0.1 +Threshold: 0.1% Annotation: on -------------------------------------------------------------------------------- @@ -24,26 +24,26 @@ Ir_______ I1mr ILmr -------------------------------------------------------------------------------- Ir_______ I1mr ILmr file:function -> 5,000,015 1 1 a.c:main +< 5,000,015 1 1 a.c:main -> 76,688 32 32 /build/glibc-OTsEL5/glibc-2.27/elf/dl-lookup.c: +< 76,688 32 32 /build/glibc-OTsEL5/glibc-2.27/elf/dl-lookup.c: 47,993 19 19 do_lookup_x 28,534 11 11 _dl_lookup_symbol_x -> 28,391 11 9 /build/glibc-OTsEL5/glibc-2.27/elf/dl-tunables.c: +< 28,391 11 9 /build/glibc-OTsEL5/glibc-2.27/elf/dl-tunables.c: 28,136 7 7 __GI___tunables_init -> 25,408 47 47 /build/glibc-OTsEL5/glibc-2.27/string/../sysdeps/x86_64/strcmp.S:strcmp +< 25,408 47 47 /build/glibc-OTsEL5/glibc-2.27/string/../sysdeps/x86_64/strcmp.S:strcmp -> 22,214 25 25 /build/glibc-OTsEL5/glibc-2.27/elf/../sysdeps/x86_64/dl-machine.h: +< 22,214 25 25 /build/glibc-OTsEL5/glibc-2.27/elf/../sysdeps/x86_64/dl-machine.h: 21,821 23 23 _dl_relocate_object -> 11,817 16 16 /build/glibc-OTsEL5/glibc-2.27/elf/do-rel.h: +< 11,817 16 16 /build/glibc-OTsEL5/glibc-2.27/elf/do-rel.h: 11,521 15 15 _dl_relocate_object -> 8,055 0 0 /build/glibc-OTsEL5/glibc-2.27/elf/dl-tunables.h:__GI___tunables_init +< 8,055 0 0 /build/glibc-OTsEL5/glibc-2.27/elf/dl-tunables.h:__GI___tunables_init -> 6,939 5 5 /build/glibc-OTsEL5/glibc-2.27/elf/dl-misc.c: +< 6,939 5 5 /build/glibc-OTsEL5/glibc-2.27/elf/dl-misc.c: 6,898 2 2 _dl_name_match_p -------------------------------------------------------------------------------- @@ -74,37 +74,44 @@ Ir_______ I1mr ILmr -------------------------------------------------------------------------------- -- Annotated source file: /build/glibc-OTsEL5/glibc-2.27/elf/../sysdeps/x86_64/dl-machine.h -------------------------------------------------------------------------------- -This file was unreadable +Unannotated because one or more of these original files are unreadable: +- /build/glibc-OTsEL5/glibc-2.27/elf/../sysdeps/x86_64/dl-machine.h -------------------------------------------------------------------------------- -- Annotated source file: /build/glibc-OTsEL5/glibc-2.27/elf/dl-lookup.c -------------------------------------------------------------------------------- -This file was unreadable +Unannotated because one or more of these original files are unreadable: +- /build/glibc-OTsEL5/glibc-2.27/elf/dl-lookup.c -------------------------------------------------------------------------------- -- Annotated source file: /build/glibc-OTsEL5/glibc-2.27/elf/dl-misc.c -------------------------------------------------------------------------------- -This file was unreadable +Unannotated because one or more of these original files are unreadable: +- /build/glibc-OTsEL5/glibc-2.27/elf/dl-misc.c -------------------------------------------------------------------------------- -- Annotated source file: /build/glibc-OTsEL5/glibc-2.27/elf/dl-tunables.c -------------------------------------------------------------------------------- -This file was unreadable +Unannotated because one or more of these original files are unreadable: +- /build/glibc-OTsEL5/glibc-2.27/elf/dl-tunables.c -------------------------------------------------------------------------------- -- Annotated source file: /build/glibc-OTsEL5/glibc-2.27/elf/dl-tunables.h -------------------------------------------------------------------------------- -This file was unreadable +Unannotated because one or more of these original files are unreadable: +- /build/glibc-OTsEL5/glibc-2.27/elf/dl-tunables.h -------------------------------------------------------------------------------- -- Annotated source file: /build/glibc-OTsEL5/glibc-2.27/elf/do-rel.h -------------------------------------------------------------------------------- -This file was unreadable +Unannotated because one or more of these original files are unreadable: +- /build/glibc-OTsEL5/glibc-2.27/elf/do-rel.h -------------------------------------------------------------------------------- -- Annotated source file: /build/glibc-OTsEL5/glibc-2.27/string/../sysdeps/x86_64/strcmp.S -------------------------------------------------------------------------------- -This file was unreadable +Unannotated because one or more of these original files are unreadable: +- /build/glibc-OTsEL5/glibc-2.27/string/../sysdeps/x86_64/strcmp.S -------------------------------------------------------------------------------- -- Annotated source file: a.c @@ -126,6 +133,7 @@ Ir_______ I1mr ILmr 5,000,015 1 1 annotated: files known & above threshold & readable, line numbers known 0 0 0 annotated: files known & above threshold & readable, line numbers unknown + 0 0 0 unannotated: files known & above threshold & two or more non-identical 179,512 136 134 unannotated: files known & above threshold & unreadable 49,754 770 758 unannotated: files known & below threshold 472 45 38 unannotated: files unknown diff --git a/cachegrind/tests/ann1a.vgtest b/cachegrind/tests/ann1a.vgtest index 740522f6dd..16da66699e 100644 --- a/cachegrind/tests/ann1a.vgtest +++ b/cachegrind/tests/ann1a.vgtest @@ -1,6 +1,8 @@ # The `prog` doesn't matter because we don't use its output. Instead we test -# the post-processing of the `ann1.cgout` file. +# the post-processing of the cgout file. prog: ../../tests/true vgopts: --cachegrind-out-file=cachegrind.out + post: touch ann1.cgout && python3 ../cg_annotate --show=Ir,I1mr,ILmr --show-percs=no ann1.cgout + cleanup: rm cachegrind.out diff --git a/cachegrind/tests/ann1b.post.exp b/cachegrind/tests/ann1b.post.exp index 3ec4288cb4..3af40a7678 100644 --- a/cachegrind/tests/ann1b.post.exp +++ b/cachegrind/tests/ann1b.post.exp @@ -1,15 +1,15 @@ -------------------------------------------------------------------------------- -- Metadata -------------------------------------------------------------------------------- +Invocation: ../cg_annotate --sort=Dr --show=Dw,Dr,Ir --auto=no ann1.cgout I1 cache: 32768 B, 64 B, 8-way associative D1 cache: 32768 B, 64 B, 8-way associative LL cache: 19922944 B, 64 B, 19-way associative Command: ./a.out -Data file: ann1.cgout Events recorded: Ir I1mr ILmr Dr D1mr DLmr Dw D1mw DLmw Events shown: Dw Dr Ir Event sort order: Dr -Threshold: 0.1 +Threshold: 0.1% Annotation: off -------------------------------------------------------------------------------- @@ -24,19 +24,19 @@ Dw_____________ Dr________________ Ir________________ -------------------------------------------------------------------------------- Dw__________________ Dr______________________ Ir______________________ file:function -> 3 (0.0%, 0.0%) 4,000,004 (98.6%, 98.6%) 5,000,015 (95.6%, 95.6%) a.c:main +< 3 (0.0%, 0.0%) 4,000,004 (98.6%, 98.6%) 5,000,015 (95.6%, 95.6%) a.c:main -> 7,668 (42.6%, 42.6%) 23,365 (0.6%, 99.1%) 76,688 (1.5%, 97.1%) /build/glibc-OTsEL5/glibc-2.27/elf/dl-lookup.c: +< 7,668 (42.6%, 42.6%) 23,365 (0.6%, 99.1%) 76,688 (1.5%, 97.1%) /build/glibc-OTsEL5/glibc-2.27/elf/dl-lookup.c: 4,543 (25.2%) 17,566 (0.4%) 47,993 (0.9%) do_lookup_x 3,083 (17.1%) 5,750 (0.1%) 28,534 (0.5%) _dl_lookup_symbol_x -> 22 (0.1%, 42.7%) 5,577 (0.1%, 99.3%) 28,391 (0.5%, 97.6%) /build/glibc-OTsEL5/glibc-2.27/elf/dl-tunables.c: +< 22 (0.1%, 42.7%) 5,577 (0.1%, 99.3%) 28,391 (0.5%, 97.6%) /build/glibc-OTsEL5/glibc-2.27/elf/dl-tunables.c: 8 (0.0%) 5,521 (0.1%) 28,136 (0.5%) __GI___tunables_init -> 2,542 (14.1%, 56.8%) 5,343 (0.1%, 99.4%) 22,214 (0.4%, 98.0%) /build/glibc-OTsEL5/glibc-2.27/elf/../sysdeps/x86_64/dl-machine.h: +< 2,542 (14.1%, 56.8%) 5,343 (0.1%, 99.4%) 22,214 (0.4%, 98.0%) /build/glibc-OTsEL5/glibc-2.27/elf/../sysdeps/x86_64/dl-machine.h: 2,490 (13.8%) 5,219 (0.1%) 21,821 (0.4%) _dl_relocate_object -> 0 (0.0%, 56.8%) 5,158 (0.1%, 99.5%) 25,408 (0.5%, 98.5%) /build/glibc-OTsEL5/glibc-2.27/string/../sysdeps/x86_64/strcmp.S:strcmp +< 0 (0.0%, 56.8%) 5,158 (0.1%, 99.5%) 25,408 (0.5%, 98.5%) /build/glibc-OTsEL5/glibc-2.27/string/../sysdeps/x86_64/strcmp.S:strcmp -------------------------------------------------------------------------------- -- Function:file summary diff --git a/cachegrind/tests/ann1b.vgtest b/cachegrind/tests/ann1b.vgtest index 320b7f3714..0aa3a1e212 100644 --- a/cachegrind/tests/ann1b.vgtest +++ b/cachegrind/tests/ann1b.vgtest @@ -1,6 +1,8 @@ # The `prog` doesn't matter because we don't use its output. Instead we test -# the post-processing of the `ann1.cgout` file. +# the post-processing of the cgout file. prog: ../../tests/true vgopts: --cachegrind-out-file=cachegrind.out + post: touch ann1.cgout && python3 ../cg_annotate --sort=Dr --show=Dw,Dr,Ir --auto=no ann1.cgout + cleanup: rm cachegrind.out diff --git a/cachegrind/tests/ann2.post.exp b/cachegrind/tests/ann2.post.exp index 8db35f136f..0a8fd98db1 100644 --- a/cachegrind/tests/ann2.post.exp +++ b/cachegrind/tests/ann2.post.exp @@ -1,12 +1,12 @@ -------------------------------------------------------------------------------- -- Metadata -------------------------------------------------------------------------------- +Invocation: ../cg_annotate --context 2 --annotate --show-percs=yes --threshold=0.5 ann2.cgout Command: ann2 -Data file: ann2.cgout Events recorded: A SomeCount VeryLongEventName Events shown: A SomeCount VeryLongEventName Event sort order: A SomeCount VeryLongEventName -Threshold: 0.5 +Threshold: 0.5% Annotation: on -------------------------------------------------------------------------------- @@ -21,24 +21,24 @@ A_______________ SomeCount_______ VeryLongEventName -------------------------------------------------------------------------------- A___________________________ SomeCount____________ VeryLongEventName__ file:function -> 86,590 (86.6%, 86.6%) 93,000 (93.0%, 93.0%) 0 (n/a, n/a) ann2-basic.rs: +< 86,590 (86.6%, 86.6%) 93,000 (93.0%, 93.0%) 0 (n/a, n/a) ann2-basic.rs: 68,081 (68.1%) 90,291 (90.3%) 0 f0 15,000 (15.0%) 600 (0.6%) 0 f1 2,000 (2.0%) 100 (0.1%) 0 f2 500 (0.5%) 0 0 f6 500 (0.5%) 0 0 f4 -> 9,000 (9.0%, 95.6%) 6,000 (6.0%, 99.0%) 0 (n/a, n/a) ann2-could-not-be-found.rs:f1 +< 9,000 (9.0%, 95.6%) 6,000 (6.0%, 99.0%) 0 (n/a, n/a) ann2-could-not-be-found.rs:f1 -> 2,000 (2.0%, 97.6%) 800 (0.8%, 99.8%) -1,000 (n/a, n/a) ann2-past-the-end.rs: +< 2,000 (2.0%, 97.6%) 800 (0.8%, 99.8%) -1,000 (n/a, n/a) ann2-past-the-end.rs: 1,000 (1.0%) 500 (0.5%) 0 1,000 (1.0%) 300 (0.3%) -1,000 (n/a) f1 -> 1,000 (1.0%, 98.6%) 0 (0.0%, 99.8%) 0 (n/a, n/a) ann2-more-recent-than-cgout.rs:new +< 1,000 (1.0%, 98.6%) 0 (0.0%, 99.8%) 0 (n/a, n/a) ann2-more-recent-than-cgout.rs:new -> 1,000 (1.0%, 99.6%) 0 (0.0%, 99.8%) 0 (n/a, n/a) ???:unknown +< 1,000 (1.0%, 99.6%) 0 (0.0%, 99.8%) 0 (n/a, n/a) ???:unknown -> 10 (0.0%, 99.6%) 0 (0.0%, 99.8%) 1,000 (n/a, n/a) ann2-negatives.rs: +< 10 (0.0%, 99.6%) 0 (0.0%, 99.8%) 1,000 (n/a, n/a) ann2-negatives.rs: -1,000,000 (-1000.0%) 0 150,000 (n/a) neg3 500,000 (500.0%) 0 -150,000 (n/a) neg2a 499,999 (500.0%) 0 0 neg2b @@ -104,7 +104,8 @@ A_____________ SomeCount_____ VeryLongEventName -------------------------------------------------------------------------------- -- Annotated source file: ann2-could-not-be-found.rs -------------------------------------------------------------------------------- -This file was unreadable +Unannotated because one or more of these original files are unreadable: +- ann2-could-not-be-found.rs -------------------------------------------------------------------------------- -- Annotated source file: ann2-more-recent-than-cgout.rs @@ -112,7 +113,8 @@ This file was unreadable @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@ WARNING @@ WARNING @@ WARNING @@ WARNING @@ WARNING @@ WARNING @@ WARNING @@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ -@ Source file 'ann2-more-recent-than-cgout.rs' is newer than data file 'ann2.cgout'. +@ Original source files are all newer than data file 'ann2.cgout': +@ - ann2-more-recent-than-cgout.rs @ Annotations may not be correct. @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@ -176,6 +178,7 @@ A_____________ SomeCount_____ VeryLongEventName 84,500 (84.5%) 94,700 (94.7%) 990 (n/a) annotated: files known & above threshold & readable, line numbers known 5,100 (5.1%) -900 (-0.9%) -990 (n/a) annotated: files known & above threshold & readable, line numbers unknown + 0 0 0 unannotated: files known & above threshold & two or more non-identical 9,000 (9.0%) 6,000 (6.0%) 0 unannotated: files known & above threshold & unreadable 400 (0.4%) 200 (0.2%) 0 unannotated: files known & below threshold 1,000 (1.0%) 0 0 unannotated: files unknown diff --git a/cachegrind/tests/ann2.vgtest b/cachegrind/tests/ann2.vgtest index 4add2fe4cc..5598bdc4bd 100644 --- a/cachegrind/tests/ann2.vgtest +++ b/cachegrind/tests/ann2.vgtest @@ -2,7 +2,7 @@ # more details. # # The `prog` doesn't matter because we don't use its output. Instead we test -# the post-processing of the `ann2.cgout` file. +# the post-processing of the cgout file. prog: ../../tests/true vgopts: --cachegrind-out-file=cachegrind.out