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`.
This commit is contained in:
Nicholas Nethercote 2023-04-12 10:02:13 +10:00
parent a2af9adec4
commit 1fdf0e728a
34 changed files with 922 additions and 255 deletions

View File

@ -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. `(?<!\\)/` is an
# example of negative lookbehind. It means "match a forward slash
# unless preceded by a backslash".
m = re.match(r"s/(.*)(?<!\\)/(.*)(?<!\\)/(g|i|gi|ig|)$", regex)
if m is None:
raise ValueError
# Forward slashes must be escaped in an `s/old/new/` expression,
# but we then must unescape them before using them with `re.sub`.
pat = m.group(1).replace(r"\/", r"/")
repl = m.group(2).replace(r"\/", r"/")
tail = m.group(3)
if "g" in tail:
count = 0 # unlimited
else:
count = 1
if "i" in tail:
flags = re.IGNORECASE
else:
flags = re.RegexFlag(0)
return lambda s: re.sub(re.compile(pat, flags=flags), repl, s, count=count)
def comma_separated_list(values: str) -> 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]
# 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 die(msg: str) -> NoReturn:
print("cg_annotate: error:", msg, file=sys.stderr)
sys.exit(1)
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 = ""
# The current Mfl and Mfn.
mfl = ""
mfn = ""
# 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
# 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 = "<unspecified>"
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 = "<unspecified>"
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(src_filename: str) -> None:
print_fancy(f"Annotated source file: {src_filename}")
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
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)

View File

@ -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. `(?<!\\)/` is an
# Extract the parts of an `s/old/new/tail` regex. `(?<!\\)/` is an
# example of negative lookbehind. It means "match a forward slash
# unless preceded by a backslash".
m = re.match(r"s/(.*)(?<!\\)/(.*)(?<!\\)/(g|i|gi|ig|)$", regex)
@ -74,7 +74,7 @@ class Args(Namespace):
raise ValueError
# Forward slashes must be escaped in an `s/old/new/` expression,
# but we then must unescape them before using them with `re.sub`
# but we then must unescape them before using them with `re.sub`.
pat = m.group(1).replace(r"\/", r"/")
repl = m.group(2).replace(r"\/", r"/")
tail = m.group(3)
@ -91,7 +91,11 @@ class Args(Namespace):
return lambda s: re.sub(re.compile(pat, flags=flags), repl, s, count=count)
p = ArgumentParser(description="Diff two Cachegrind output files.")
desc = (
"Diff two Cachegrind output files. Deprecated; use "
"`cg_annotate --diff` instead."
)
p = ArgumentParser(description=desc)
p.add_argument("--version", action="version", version="%(prog)s-@VERSION@")
@ -304,8 +308,8 @@ def main() -> 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():

View File

@ -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.

View File

@ -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 \

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,4 +1,4 @@
desc: Description for ann-diff2a.cgout
desc: Description for ann-diff2b.cgout
cmd: cmd2
events: One Two

View File

@ -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

View File

@ -0,0 +1,3 @@
I refs:

View File

@ -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

View File

@ -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%) <unknown (line 0)>
-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

View File

@ -0,0 +1,3 @@
I refs:

View File

@ -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

View File

@ -0,0 +1,3 @@
one
two
three

View File

@ -0,0 +1,5 @@
one
two
three
four
five

View File

@ -0,0 +1,5 @@
ONE
TWO
THREE
FOUR
FIVE

View File

@ -0,0 +1,3 @@
one
two
three

View File

@ -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

View File

@ -0,0 +1,5 @@
one
two
three
four
five

View File

@ -0,0 +1,6 @@
ONE
TWO
THREE
FOUR
FIVE
SIX

View File

@ -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

View File

@ -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

View File

@ -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
post: python3 ../cg_merge ann-merge1a.cgout ann-merge1b.cgout > ann-merge1c.cgout && python3 ../cg_annotate ann-merge1c.cgout
cleanup: rm cachegrind.out ann-merge1c.cgout

View File

@ -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

View File

@ -0,0 +1,3 @@
I refs:

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 <unspecified>
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

View File

@ -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