mirror of
git://sourceware.org/git/valgrind.git
synced 2026-01-12 00:19:31 +08:00
Overhaul cg_annotate output.
Most notable, the "Function summary" section, which printed one CC for each
`file:function` combination, has been replaced by two sections, "File:function
summary" and "Function:file summary".
These new sections both feature "deep CCs", which have an "outer CC" for the
file (or function), and one or more "inner CCs" for the paired functions (or
files).
Here is a file:function example, which helps show which files have a lot of
events, even if those events are spread across a lot of functions.
```
> 12,427,830 (5.4%, 26.3%) /home/njn/moz/gecko-dev/js/src/ds/LifoAlloc.h:
6,107,862 (2.7%) js::frontend::ParseNodeVerifier::visit(js::frontend::ParseNode*)
3,685,203 (1.6%) js::detail::BumpChunk::setBump(unsigned char*)
1,640,591 (0.7%) js::LifoAlloc::alloc(unsigned long)
711,008 (0.3%) js::detail::BumpChunk::assertInvariants()
```
And here is a function:file example, which shows how heavy inlining can result
in a machine code function being derived from source code from multiple files:
```
> 1,343,736 (0.6%, 35.6%) js::gc::TenuredCell::isMarkedGray() const:
651,108 (0.3%) /home/njn/moz/gecko-dev/js/src/d64/dist/include/js/HeapAPI.h
292,672 (0.1%) /home/njn/moz/gecko-dev/js/src/gc/Cell.h
254,854 (0.1%) /home/njn/moz/gecko-dev/js/src/gc/Heap.h
```
Previously these patterns were very hard to find, and it was easy to overlook a
hot piece of code because its counts were spread across multiple non-adjacent
entries. I have already found these changes very useful for profiling Rust
code.
Also, cumulative percentages on the outer CCs (e.g. the 26.3% and 35.6% in the
example) tell you what fraction of all events are covered by the entries so
far, something I've wanted for a long time.
Some other, related changes:
- Column event headers are now padded with `_`, e.g. `Ir__________`. This makes
the column/event mapping clearer.
- The "Cachegrind profile" section is now called "Metadata", which is
shorter and clearer.
- A few minor test tweaks, beyond those required for the output changes.
- I converted some doc comments to normal comments. Not standard Python, but
nicer to read, and there are no public APIs here.
- Roughly 2x speedups to `cg_annotate` and smaller improvements for `cg_diff`
and `cg_merge`, due to the following.
- Change the `Cc` class to a type alias for `list[int]`, to avoid the class
overhead (sigh).
- Process event count lines in a single split, instead of a regex
match + split.
- Add the `add_cc_to_ccs` function, which does multiple CC additions in a
single function call.
- Better handling of dicts while reading input, minimizing lookups.
- Pre-computing the missing CC string for each CcPrinter, instead of
regenerating it each time.
This commit is contained in:
@@ -12,10 +12,12 @@
|
||||
[MESSAGES CONTROL]
|
||||
|
||||
disable=
|
||||
# We don't care about having docstrings for all functions/classes.
|
||||
# We don't care about having docstrings everywhere.
|
||||
missing-class-docstring, missing-function-docstring,
|
||||
# We don't care about large functions, sometimes it's necessary.
|
||||
too-many-branches, too-many-locals, too-many-statements,
|
||||
missing-module-docstring,
|
||||
# We don't care about these, sometimes they are necessary.
|
||||
too-many-arguments, too-many-branches, too-many-lines, too-many-locals,
|
||||
too-many-statements,
|
||||
# Zero or one public methods in a class is fine.
|
||||
too-few-public-methods,
|
||||
|
||||
|
||||
@@ -26,15 +26,12 @@
|
||||
#
|
||||
# The GNU General Public License is contained in the file COPYING.
|
||||
|
||||
"""
|
||||
This script reads Cachegrind output files and produces human-readable reports.
|
||||
"""
|
||||
|
||||
# This script reads Cachegrind output files and produces human-readable output.
|
||||
#
|
||||
# Use `make pyann` to "build" this script with `auxprogs/pybuild.rs` every time
|
||||
# it is changed. This runs the formatters, type-checkers, and linters on
|
||||
# `cg_annotate.in` and then generates `cg_annotate`.
|
||||
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
@@ -42,16 +39,12 @@ import re
|
||||
import sys
|
||||
from argparse import ArgumentParser, BooleanOptionalAction, Namespace
|
||||
from collections import defaultdict
|
||||
from typing import DefaultDict, NewType, NoReturn, TextIO
|
||||
from typing import DefaultDict, NoReturn, TextIO
|
||||
|
||||
|
||||
# A typed wrapper for parsed args.
|
||||
class Args(Namespace):
|
||||
"""
|
||||
A typed wrapper for parsed args.
|
||||
|
||||
None of these fields are modified after arg parsing finishes.
|
||||
"""
|
||||
|
||||
# None of these fields are modified after arg parsing finishes.
|
||||
show: list[str]
|
||||
sort: list[str]
|
||||
threshold: float # a percentage
|
||||
@@ -72,16 +65,14 @@ class Args(Namespace):
|
||||
return f
|
||||
raise ValueError
|
||||
|
||||
# Add a bool argument that defaults to true.
|
||||
#
|
||||
# Supports these forms: `--foo`, `--no-foo`, `--foo=yes`, `--foo=no`.
|
||||
# The latter two were the forms supported by the old Perl version of
|
||||
# `cg_annotate`, and are now deprecated.
|
||||
def add_bool_argument(
|
||||
p: ArgumentParser, new_name: str, old_name: str, help_: str
|
||||
) -> None:
|
||||
"""
|
||||
Add a bool argument that defaults to true.
|
||||
|
||||
Supports these forms: `--foo`, `--no-foo`, `--foo=yes`, `--foo=no`.
|
||||
The latter two were the forms supported by the old Perl version of
|
||||
`cg_annotate`, and are now deprecated.
|
||||
"""
|
||||
new_flag = "--" + new_name
|
||||
old_flag = "--" + old_name
|
||||
dest = new_name.replace("-", "_")
|
||||
@@ -110,28 +101,25 @@ class Args(Namespace):
|
||||
p = ArgumentParser(description="Process a Cachegrind output file.")
|
||||
|
||||
p.add_argument("--version", action="version", version="%(prog)s-@VERSION@")
|
||||
|
||||
p.add_argument(
|
||||
"--show",
|
||||
type=comma_separated_list,
|
||||
metavar="A,B,C",
|
||||
help="only show figures for events A,B,C (default: all events)",
|
||||
)
|
||||
|
||||
p.add_argument(
|
||||
"--sort",
|
||||
type=comma_separated_list,
|
||||
metavar="A,B,C",
|
||||
help="sort functions by events A,B,C (default: event column order)",
|
||||
)
|
||||
|
||||
p.add_argument(
|
||||
"--threshold",
|
||||
type=threshold,
|
||||
default=0.1,
|
||||
metavar="N:[0,20]",
|
||||
help="only show functions with more than N%% of primary sort event "
|
||||
"counts (default: %(default)s)",
|
||||
help="only show file:function/function:file pairs with more than "
|
||||
"N%% of primary sort event counts (default: %(default)s)",
|
||||
)
|
||||
add_bool_argument(
|
||||
p,
|
||||
@@ -182,6 +170,9 @@ class Events:
|
||||
# The event names.
|
||||
events: list[str]
|
||||
|
||||
# Equal to `len(self.events)`.
|
||||
num_events: int
|
||||
|
||||
# The order in which we must traverse events for --show. Can be shorter
|
||||
# than `events`.
|
||||
show_events: list[str]
|
||||
@@ -226,10 +217,10 @@ class Events:
|
||||
|
||||
self.sort_indices = [event_indices[event] for event in self.sort_events]
|
||||
|
||||
def mk_cc(self, text: str) -> Cc:
|
||||
"""Raises a `ValueError` exception on syntax error."""
|
||||
# Raises a `ValueError` exception on syntax error.
|
||||
def mk_cc(self, str_counts: list[str]) -> Cc:
|
||||
# This is slightly faster than a list comprehension.
|
||||
counts = list(map(int, text.split()))
|
||||
counts = list(map(int, str_counts))
|
||||
|
||||
if len(counts) == self.num_events:
|
||||
pass
|
||||
@@ -239,48 +230,81 @@ class Events:
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
return Cc(counts)
|
||||
return counts
|
||||
|
||||
def mk_empty_cc(self) -> Cc:
|
||||
# This is much faster than a list comprehension.
|
||||
return Cc([0] * self.num_events)
|
||||
return [0] * self.num_events
|
||||
|
||||
def mk_empty_dcc(self) -> Dcc:
|
||||
return Dcc(self.mk_empty_cc(), defaultdict(self.mk_empty_cc))
|
||||
|
||||
|
||||
class Cc:
|
||||
"""
|
||||
This is a dumb container for counts.
|
||||
# A "cost centre", which is a dumb container for counts. Always the same length
|
||||
# as `Events.events`, but it doesn't even know event names. `Events.mk_cc` and
|
||||
# `Events.mk_empty_cc` are used for construction.
|
||||
#
|
||||
# This used to be a class with a single field `counts: list[int]`, but this
|
||||
# type is very hot and just using a type alias is much faster.
|
||||
Cc = list[int]
|
||||
|
||||
It doesn't know anything about events, i.e. what each count means. It can
|
||||
do basic operations like `__iadd__` and `__eq__`, and anything more must be
|
||||
done elsewhere. `Events.mk_cc` and `Events.mk_empty_cc` are used for
|
||||
construction.
|
||||
"""
|
||||
|
||||
# Always the same length as `Events.events`.
|
||||
counts: list[int]
|
||||
|
||||
def __init__(self, counts: list[int]) -> None:
|
||||
self.counts = counts
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return str(self.counts)
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if not isinstance(other, Cc):
|
||||
return NotImplemented
|
||||
return self.counts == other.counts
|
||||
|
||||
def __iadd__(self, other: Cc) -> Cc:
|
||||
for i, other_count in enumerate(other.counts):
|
||||
self.counts[i] += other_count
|
||||
return self
|
||||
# Add the counts in `a_cc` to `b_cc`.
|
||||
def add_cc_to_cc(a_cc: Cc, b_cc: Cc) -> None:
|
||||
for i, a_count in enumerate(a_cc):
|
||||
b_cc[i] += a_count
|
||||
|
||||
|
||||
# A paired filename and function name.
|
||||
Flfn = NewType("Flfn", tuple[str, str])
|
||||
# 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
|
||||
) -> 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
|
||||
|
||||
# Per-function CCs.
|
||||
DictFlfnCc = DefaultDict[Flfn, Cc]
|
||||
|
||||
# Update `min_cc` and `max_cc` with `self`.
|
||||
def update_cc_extremes(self: Cc, min_cc: Cc, max_cc: Cc) -> None:
|
||||
for i, count in enumerate(self):
|
||||
if count > max_cc[i]:
|
||||
max_cc[i] = count
|
||||
elif count < min_cc[i]:
|
||||
min_cc[i] = count
|
||||
|
||||
|
||||
# A deep cost centre with a dict for the inner names and CCs.
|
||||
class Dcc:
|
||||
outer_cc: Cc
|
||||
inner_dict_name_cc: DictNameCc
|
||||
|
||||
def __init__(self, outer_cc: Cc, inner_dict_name_cc: DictNameCc) -> None:
|
||||
self.outer_cc = outer_cc
|
||||
self.inner_dict_name_cc = inner_dict_name_cc
|
||||
|
||||
|
||||
# A deep cost centre with a list for the inner names and CCs. Used during
|
||||
# filtering and sorting.
|
||||
class Lcc:
|
||||
outer_cc: Cc
|
||||
inner_list_name_cc: ListNameCc
|
||||
|
||||
def __init__(self, outer_cc: Cc, inner_list_name_cc: ListNameCc) -> None:
|
||||
self.outer_cc = outer_cc
|
||||
self.inner_list_name_cc = inner_list_name_cc
|
||||
|
||||
|
||||
# Per-file/function CCs. The list version is used during filtering and sorting.
|
||||
DictNameCc = DefaultDict[str, Cc]
|
||||
ListNameCc = 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-line CCs, organised by filename and line number.
|
||||
DictLineCc = DefaultDict[int, Cc]
|
||||
@@ -292,7 +316,15 @@ def die(msg: str) -> NoReturn:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def read_cgout_file() -> tuple[str, str, Events, DictFlfnCc, DictFlDictLineCc, Cc]:
|
||||
def read_cgout_file() -> tuple[
|
||||
str,
|
||||
str,
|
||||
Events,
|
||||
DictNameDcc,
|
||||
DictNameDcc,
|
||||
DictFlDictLineCc,
|
||||
Cc,
|
||||
]:
|
||||
# The file format is described in Cachegrind's manual.
|
||||
try:
|
||||
cgout_file = open(args.cgout_filename[0], "r", encoding="utf-8")
|
||||
@@ -334,52 +366,67 @@ def read_cgout_file() -> tuple[str, str, Events, DictFlfnCc, DictFlDictLineCc, C
|
||||
def mk_empty_dict_line_cc() -> DictLineCc:
|
||||
return defaultdict(events.mk_empty_cc)
|
||||
|
||||
curr_fl = ""
|
||||
curr_flfn = Flfn(("", ""))
|
||||
# The current filename and function name.
|
||||
fl = ""
|
||||
fn = ""
|
||||
|
||||
# Different places where we accumulate CC data.
|
||||
dict_flfn_cc: DictFlfnCc = defaultdict(events.mk_empty_cc)
|
||||
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
|
||||
|
||||
# Compile the one hot regex.
|
||||
count_pat = re.compile(r"(\d+)\s+(.*)")
|
||||
# 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()
|
||||
dict_line_cc = mk_empty_dict_line_cc()
|
||||
|
||||
# Line matching is done in order of pattern frequency, for speed.
|
||||
while True:
|
||||
line = readline()
|
||||
|
||||
if m := count_pat.match(line):
|
||||
line_num = int(m.group(1))
|
||||
while line := readline():
|
||||
if line[0].isdigit():
|
||||
split_line = line.split()
|
||||
try:
|
||||
cc = events.mk_cc(m.group(2))
|
||||
line_num = int(split_line[0])
|
||||
cc = events.mk_cc(split_line[1:])
|
||||
except ValueError:
|
||||
parse_die("malformed or too many event counts")
|
||||
|
||||
# Record this CC at the function level.
|
||||
flfn_cc = dict_flfn_cc[curr_flfn]
|
||||
flfn_cc += cc
|
||||
|
||||
# Record this CC at the file/line level.
|
||||
line_cc = dict_fl_dict_line_cc[curr_fl][line_num]
|
||||
line_cc += cc
|
||||
# Record this CC at the file:function level, the function:file
|
||||
# level, and the file/line level.
|
||||
add_cc_to_ccs(
|
||||
cc,
|
||||
fl_dcc.outer_cc,
|
||||
fn_dcc.outer_cc,
|
||||
fl_dcc_inner_fn_cc,
|
||||
fn_dcc_inner_fl_cc,
|
||||
dict_line_cc[line_num],
|
||||
)
|
||||
|
||||
elif line.startswith("fn="):
|
||||
curr_flfn = Flfn((curr_fl, line[3:-1]))
|
||||
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]
|
||||
|
||||
elif line.startswith("fl="):
|
||||
curr_fl = line[3:-1]
|
||||
fl = line[3:-1]
|
||||
# A `fn=` line should follow, overwriting the function name.
|
||||
curr_flfn = Flfn((curr_fl, "<unspecified>"))
|
||||
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]
|
||||
|
||||
elif m := re.match(r"summary:\s+(.*)", line):
|
||||
try:
|
||||
summary_cc = events.mk_cc(m.group(1))
|
||||
summary_cc = events.mk_cc(m.group(1).split())
|
||||
except ValueError:
|
||||
parse_die("too many event counts")
|
||||
|
||||
elif line == "":
|
||||
break # EOF
|
||||
parse_die("malformed or too many event counts")
|
||||
|
||||
elif line == "\n" or line.startswith("#"):
|
||||
# Skip empty lines and comment lines.
|
||||
@@ -392,10 +439,10 @@ def read_cgout_file() -> tuple[str, str, Events, DictFlfnCc, DictFlDictLineCc, C
|
||||
if not summary_cc:
|
||||
parse_die("missing `summary:` line, aborting")
|
||||
|
||||
# Check summary is correct.
|
||||
# Check summary is correct. (Only using the outer CCs.)
|
||||
total_cc = events.mk_empty_cc()
|
||||
for flfn_cc in dict_flfn_cc.values():
|
||||
total_cc += flfn_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"
|
||||
@@ -404,7 +451,32 @@ def read_cgout_file() -> tuple[str, str, Events, DictFlfnCc, DictFlDictLineCc, C
|
||||
)
|
||||
parse_die(msg)
|
||||
|
||||
return (desc, cmd, events, dict_flfn_cc, dict_fl_dict_line_cc, summary_cc)
|
||||
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:
|
||||
# Width of the widest commified event count.
|
||||
count: int
|
||||
|
||||
# Width of the widest first percentage, of the form ` (n.n%)` or ` (n.n%,`.
|
||||
perc1: int
|
||||
|
||||
# Width of the widest second percentage, of the form ` n.n%)`.
|
||||
perc2: int
|
||||
|
||||
def __init__(self, count: int, perc1: int, perc2: int) -> None:
|
||||
self.count = count
|
||||
self.perc1 = perc1
|
||||
self.perc2 = perc2
|
||||
|
||||
|
||||
class CcPrinter:
|
||||
@@ -414,90 +486,164 @@ class CcPrinter:
|
||||
# Note: every `CcPrinter` gets the same summary CC.
|
||||
summary_cc: Cc
|
||||
|
||||
# The width of each event count column. (This column is also used for event
|
||||
# names.) For simplicity, its length matches `events.events`, even though
|
||||
# not all events are necessarily shown.
|
||||
count_widths: list[int]
|
||||
# String to print before the event names.
|
||||
events_prefix: str
|
||||
|
||||
# The width of each percentage column. Zero if --show-percs is disabled.
|
||||
# Its length matches `count_widths`.
|
||||
perc_widths: list[int]
|
||||
# The widths of each event column. For simplicity, its length matches
|
||||
# `events.events`, even though not all events are necessarily shown.
|
||||
widths: list[Width]
|
||||
|
||||
def __init__(self, events: Events, ccs: list[Cc], summary_cc: Cc) -> None:
|
||||
# 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.
|
||||
def __init__(self, events: Events, summary_cc: Cc) -> None:
|
||||
self.events = events
|
||||
self.summary_cc = summary_cc
|
||||
# Other fields initialized in `init_*`.
|
||||
|
||||
# Find min and max value for each event. One of them will be the
|
||||
# widest value.
|
||||
min_cc = events.mk_empty_cc()
|
||||
max_cc = events.mk_empty_cc()
|
||||
def init_ccs(self, ccs: list[Cc]) -> None:
|
||||
self.events_prefix = ""
|
||||
|
||||
# Find min and max count for each event. One of them will be the widest
|
||||
# value.
|
||||
min_cc = self.events.mk_empty_cc()
|
||||
max_cc = self.events.mk_empty_cc()
|
||||
for cc in ccs:
|
||||
for i, _ in enumerate(events.events):
|
||||
count = cc.counts[i]
|
||||
if count > max_cc.counts[i]:
|
||||
max_cc.counts[i] = count
|
||||
elif count < min_cc.counts[i]:
|
||||
min_cc.counts[i] = count
|
||||
update_cc_extremes(cc, min_cc, max_cc)
|
||||
|
||||
# Find maximum width for each column.
|
||||
self.count_widths = [0] * events.num_events
|
||||
self.perc_widths = [0] * events.num_events
|
||||
for i, event in enumerate(events.events):
|
||||
# Get count and perc widths of the min and max CCs.
|
||||
(min_count, min_perc) = self.count_and_perc(min_cc, i)
|
||||
(max_count, max_perc) = self.count_and_perc(max_cc, i)
|
||||
self.init_widths(min_cc, max_cc, None, None)
|
||||
|
||||
# The event name goes in the count column.
|
||||
self.count_widths[i] = max(len(min_count), len(max_count), len(event))
|
||||
self.perc_widths[i] = max(len(min_perc), len(max_perc))
|
||||
def init_list_name_lcc(self, list_name_lcc: ListNameLcc) -> None:
|
||||
self.events_prefix = " "
|
||||
|
||||
cumul_cc = self.events.mk_empty_cc()
|
||||
|
||||
# Find min and max value for each event. One of them will be the widest
|
||||
# value. Likewise for the cumulative counts.
|
||||
min_cc = self.events.mk_empty_cc()
|
||||
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:
|
||||
# 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:
|
||||
update_cc_extremes(inner_cc, min_cc, max_cc)
|
||||
|
||||
# Consider only outer CCs for `perc2`.
|
||||
add_cc_to_cc(lcc.outer_cc, cumul_cc)
|
||||
update_cc_extremes(cumul_cc, min_cumul_cc, max_cumul_cc)
|
||||
|
||||
self.init_widths(min_cc, max_cc, min_cumul_cc, max_cumul_cc)
|
||||
|
||||
def init_widths(
|
||||
self, min_cc1: Cc, max_cc1: Cc, min_cc2: Cc | None, max_cc2: Cc | None
|
||||
) -> None:
|
||||
self.widths = [Width(0, 0, 0)] * self.events.num_events
|
||||
for i in range(len(self.events.events)):
|
||||
# Get count and percs widths of the min and max CCs.
|
||||
(min_count, min_perc1, min_perc2) = self.count_and_percs_strs(
|
||||
min_cc1, min_cc2, i
|
||||
)
|
||||
(max_count, max_perc1, max_perc2) = self.count_and_percs_strs(
|
||||
max_cc1, max_cc2, i
|
||||
)
|
||||
self.widths[i] = Width(
|
||||
max(len(min_count), len(max_count)),
|
||||
max(len(min_perc1), len(max_perc1)),
|
||||
max(len(min_perc2), len(max_perc2)),
|
||||
)
|
||||
|
||||
self.missing_cc_str = ""
|
||||
for i in self.events.show_indices:
|
||||
self.missing_cc_str += self.count_and_percs_str(i, ".", "", "")
|
||||
|
||||
# Get the count and perc string for `cc1[i]` and the perc string for
|
||||
# `cc2[i]`. (Unless `cc2` is `None`, in which case `perc2` will be "".)
|
||||
def count_and_percs_strs(
|
||||
self, cc1: Cc, cc2: Cc | None, i: int
|
||||
) -> tuple[str, str, str]:
|
||||
count = f"{cc1[i]:,d}" # commify
|
||||
if args.show_percs:
|
||||
summary_count = self.summary_cc[i]
|
||||
if cc2 is None:
|
||||
# A plain or inner CC, with a single percentage.
|
||||
if cc1[i] == 0:
|
||||
# Don't show percentages for "0" entries, it's just clutter.
|
||||
perc1 = ""
|
||||
elif summary_count == 0:
|
||||
# Avoid dividing by zero.
|
||||
perc1 = " (n/a)"
|
||||
else:
|
||||
perc1 = f" ({cc1[i] * 100 / summary_count:.1f}%)"
|
||||
perc2 = ""
|
||||
else:
|
||||
# An outer CC, with two percentages.
|
||||
if summary_count == 0:
|
||||
# Avoid dividing by zero.
|
||||
perc1 = " (n/a,"
|
||||
perc2 = " n/a)"
|
||||
else:
|
||||
perc1 = f" ({cc1[i] * 100 / summary_count:.1f}%,"
|
||||
perc2 = f" {cc2[i] * 100 / summary_count:.1f}%)"
|
||||
else:
|
||||
perc1 = ""
|
||||
perc2 = ""
|
||||
|
||||
return (count, perc1, perc2)
|
||||
|
||||
def count_and_percs_str(self, i: int, count: str, perc1: str, perc2: str) -> str:
|
||||
event_w = len(self.events.events[i])
|
||||
count_w = self.widths[i].count
|
||||
perc1_w = self.widths[i].perc1
|
||||
perc2_w = self.widths[i].perc2
|
||||
pre_w = max(0, event_w - count_w - perc1_w - perc2_w)
|
||||
return f"{'':>{pre_w}}{count:>{count_w}}{perc1:>{perc1_w}}{perc2:>{perc2_w}} "
|
||||
|
||||
def print_events(self, suffix: str) -> None:
|
||||
print(self.events_prefix, end="")
|
||||
for i in self.events.show_indices:
|
||||
# The event name goes in the count column.
|
||||
event = self.events.events[i]
|
||||
nwidth = self.count_widths[i]
|
||||
pwidth = self.perc_widths[i]
|
||||
empty_perc = ""
|
||||
print(f"{event:<{nwidth}}{empty_perc:>{pwidth}} ", end="")
|
||||
event_w = len(event)
|
||||
count_w = self.widths[i].count
|
||||
perc1_w = self.widths[i].perc1
|
||||
perc2_w = self.widths[i].perc2
|
||||
print(f"{event:_<{max(event_w, count_w + perc1_w + perc2_w)}} ", end="")
|
||||
|
||||
print(suffix)
|
||||
|
||||
def print_count_and_perc(self, i: int, count: str, perc: str) -> None:
|
||||
nwidth = self.count_widths[i]
|
||||
pwidth = self.perc_widths[i]
|
||||
print(f"{count:>{nwidth}}{perc:>{pwidth}} ", end="")
|
||||
|
||||
def count_and_perc(self, cc: Cc, i: int) -> tuple[str, str]:
|
||||
count = f"{cc.counts[i]:,d}" # commify
|
||||
if args.show_percs:
|
||||
if cc.counts[i] == 0:
|
||||
# Don't show percentages for "0" entries, it's just clutter.
|
||||
perc = ""
|
||||
else:
|
||||
summary_count = self.summary_cc.counts[i]
|
||||
if summary_count == 0:
|
||||
perc = " (n/a)"
|
||||
else:
|
||||
p = cc.counts[i] * 100 / summary_count
|
||||
perc = f" ({p:.1f}%)"
|
||||
def print_lcc(self, lcc: Lcc, outer_name: str, cumul_cc: Cc) -> None:
|
||||
print("> ", end="")
|
||||
if (
|
||||
len(lcc.inner_list_name_cc) == 1
|
||||
and lcc.outer_cc == lcc.inner_list_name_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}")
|
||||
else:
|
||||
perc = ""
|
||||
# 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:
|
||||
print(" ", end="")
|
||||
self.print_cc(inner_cc, None, f" {inner_name}")
|
||||
print()
|
||||
|
||||
return (count, perc)
|
||||
|
||||
def print_cc(self, cc: Cc, suffix: str) -> None:
|
||||
# If `cc2` is `None`, it's a vanilla CC or inner CC. Otherwise, it's an
|
||||
# outer CC.
|
||||
def print_cc(self, cc: Cc, cc2: Cc | None, suffix: str) -> None:
|
||||
for i in self.events.show_indices:
|
||||
(count, perc) = self.count_and_perc(cc, i)
|
||||
self.print_count_and_perc(i, count, perc)
|
||||
(count, perc1, perc2) = self.count_and_percs_strs(cc, cc2, i)
|
||||
print(self.count_and_percs_str(i, count, perc1, perc2), end="")
|
||||
|
||||
print("", suffix)
|
||||
|
||||
def print_missing_cc(self, suffix: str) -> None:
|
||||
# Don't show percentages for "." entries, it's just clutter.
|
||||
for i in self.events.show_indices:
|
||||
self.print_count_and_perc(i, ".", "")
|
||||
|
||||
print("", suffix)
|
||||
print(self.missing_cc_str, suffix)
|
||||
|
||||
|
||||
# Used in various places in the output.
|
||||
@@ -508,8 +654,8 @@ def print_fancy(text: str) -> None:
|
||||
print(fancy)
|
||||
|
||||
|
||||
def print_cachegrind_profile(desc: str, cmd: str, events: Events) -> None:
|
||||
print_fancy("Cachegrind profile")
|
||||
def print_metadata(desc: str, cmd: str, events: Events) -> None:
|
||||
print_fancy("Metadata")
|
||||
print(desc, end="")
|
||||
print("Command: ", cmd)
|
||||
print("Data file: ", args.cgout_filename[0])
|
||||
@@ -530,53 +676,79 @@ def print_cachegrind_profile(desc: str, cmd: str, events: Events) -> None:
|
||||
|
||||
|
||||
def print_summary(events: Events, summary_cc: Cc) -> None:
|
||||
printer = CcPrinter(events, [summary_cc], summary_cc)
|
||||
printer = CcPrinter(events, summary_cc)
|
||||
printer.init_ccs([summary_cc])
|
||||
print_fancy("Summary")
|
||||
printer.print_events("")
|
||||
print()
|
||||
printer.print_cc(summary_cc, "PROGRAM TOTALS")
|
||||
printer.print_cc(summary_cc, None, "PROGRAM TOTALS")
|
||||
print()
|
||||
|
||||
|
||||
def print_function_summary(
|
||||
events: Events, dict_flfn_cc: DictFlfnCc, summary_cc: Cc
|
||||
def print_name_summary(
|
||||
kind: str, events: Events, dict_name_dcc: DictNameDcc, summary_cc: Cc
|
||||
) -> set[str]:
|
||||
# Only the first threshold percentage is actually used.
|
||||
# The primary sort event is used for the threshold.
|
||||
threshold_index = events.sort_indices[0]
|
||||
|
||||
# Convert the threshold from a percentage to an event count.
|
||||
threshold = args.threshold * abs(summary_cc.counts[threshold_index]) / 100
|
||||
threshold = args.threshold * abs(summary_cc[threshold_index]) / 100
|
||||
|
||||
def meets_threshold(flfn_and_cc: tuple[Flfn, Cc]) -> bool:
|
||||
cc = flfn_and_cc[1]
|
||||
return abs(cc.counts[threshold_index]) >= threshold
|
||||
def meets_threshold(name_and_cc: tuple[str, Cc]) -> bool:
|
||||
cc = name_and_cc[1]
|
||||
return abs(cc[threshold_index]) >= threshold
|
||||
|
||||
# Create a list with the counts in sort order, so that left-to-right list
|
||||
# comparison does the right thing. Plus the `Flfn` at the end for
|
||||
# deterministic output when all the event counts are identical in two CCs.
|
||||
def key(flfn_and_cc: tuple[Flfn, Cc]) -> tuple[list[int], Flfn]:
|
||||
cc = flfn_and_cc[1]
|
||||
return ([abs(cc.counts[i]) for i in events.sort_indices], flfn_and_cc[0])
|
||||
# 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
|
||||
return (
|
||||
[abs(lcc.outer_cc[i]) for i in events.sort_indices],
|
||||
outer_name,
|
||||
)
|
||||
|
||||
# Filter out functions for which the primary sort event count is below the
|
||||
# threshold, and sort the remainder.
|
||||
filtered_flfns_and_ccs = filter(meets_threshold, dict_flfn_cc.items())
|
||||
sorted_flfns_and_ccs = sorted(filtered_flfns_and_ccs, key=key, reverse=True)
|
||||
sorted_ccs = list(map(lambda flfn_and_cc: flfn_and_cc[1], sorted_flfns_and_ccs))
|
||||
# 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)
|
||||
|
||||
printer = CcPrinter(events, sorted_ccs, summary_cc)
|
||||
print_fancy("Function summary")
|
||||
printer.print_events(" file:function")
|
||||
# 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():
|
||||
# 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,
|
||||
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:
|
||||
continue
|
||||
|
||||
list_name_lcc.append((outer_name, Lcc(dcc.outer_cc, inner_list_name_cc)))
|
||||
|
||||
list_name_lcc = sorted(list_name_lcc, key=key_name_and_lcc, reverse=True)
|
||||
|
||||
printer = CcPrinter(events, summary_cc)
|
||||
printer.init_list_name_lcc(list_name_lcc)
|
||||
print_fancy(kind + " summary")
|
||||
printer.print_events(" " + kind.lower())
|
||||
print()
|
||||
|
||||
# Print per-function counts.
|
||||
for flfn, flfn_cc in sorted_flfns_and_ccs:
|
||||
printer.print_cc(flfn_cc, f"{flfn[0]}:{flfn[1]}")
|
||||
# Print LCCs.
|
||||
threshold_names = set([])
|
||||
cumul_cc = events.mk_empty_cc()
|
||||
for name, lcc in list_name_lcc:
|
||||
add_cc_to_cc(lcc.outer_cc, cumul_cc)
|
||||
printer.print_lcc(lcc, name, cumul_cc)
|
||||
threshold_names.add(name)
|
||||
|
||||
print()
|
||||
|
||||
# Files containing a function that met the threshold.
|
||||
return set(flfn_and_cc[0][0] for flfn_and_cc in sorted_flfns_and_ccs)
|
||||
return threshold_names
|
||||
|
||||
|
||||
class AnnotatedCcs:
|
||||
@@ -647,7 +819,8 @@ def print_annotated_src_file(
|
||||
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, list(dict_line_cc.values()), summary_cc)
|
||||
printer = CcPrinter(events, summary_cc)
|
||||
printer.init_ccs(list(dict_line_cc.values()))
|
||||
# The starting fancy has already been printed by the caller.
|
||||
printer.print_events("")
|
||||
print()
|
||||
@@ -658,8 +831,8 @@ def print_annotated_src_file(
|
||||
line0_cc = dict_line_cc.pop(0, None)
|
||||
if line0_cc:
|
||||
suffix = "<unknown (line 0)>"
|
||||
printer.print_cc(line0_cc, suffix)
|
||||
annotated_ccs.line_nums_unknown_cc += line0_cc
|
||||
printer.print_cc(line0_cc, None, suffix)
|
||||
add_cc_to_cc(line0_cc, annotated_ccs.line_nums_unknown_cc)
|
||||
print()
|
||||
|
||||
# Find interesting line ranges: all lines with a CC, and all lines within
|
||||
@@ -698,8 +871,10 @@ def print_annotated_src_file(
|
||||
if not src_line:
|
||||
return # EOF
|
||||
if line_nums and line_num == line_nums[0]:
|
||||
printer.print_cc(dict_line_cc[line_num], src_line[:-1])
|
||||
annotated_ccs.line_nums_known_cc += dict_line_cc[line_num]
|
||||
printer.print_cc(dict_line_cc[line_num], None, src_line[:-1])
|
||||
add_cc_to_cc(
|
||||
dict_line_cc[line_num], annotated_ccs.line_nums_known_cc
|
||||
)
|
||||
del line_nums[0]
|
||||
else:
|
||||
printer.print_missing_cc(src_line[:-1])
|
||||
@@ -715,8 +890,10 @@ def print_annotated_src_file(
|
||||
if line_nums:
|
||||
print()
|
||||
for line_num in line_nums:
|
||||
printer.print_cc(dict_line_cc[line_num], f"<bogus line {line_num}>")
|
||||
annotated_ccs.line_nums_known_cc += dict_line_cc[line_num]
|
||||
printer.print_cc(
|
||||
dict_line_cc[line_num], None, f"<bogus line {line_num}>"
|
||||
)
|
||||
add_cc_to_cc(dict_line_cc[line_num], annotated_ccs.line_nums_known_cc)
|
||||
|
||||
print()
|
||||
warn_bogus_lines(src_file.name)
|
||||
@@ -736,7 +913,7 @@ def print_annotated_src_files(
|
||||
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():
|
||||
accum_cc += line_cc
|
||||
add_cc_to_cc(line_cc, accum_cc)
|
||||
|
||||
# Exclude the unknown ("???") file, which is unannotatable.
|
||||
ann_src_filenames.discard("???")
|
||||
@@ -771,7 +948,6 @@ def print_annotated_src_files(
|
||||
annotated_ccs,
|
||||
summary_cc,
|
||||
)
|
||||
|
||||
readable = True
|
||||
break
|
||||
except OSError:
|
||||
@@ -799,15 +975,16 @@ def print_annotation_summary(
|
||||
summary_cc: Cc,
|
||||
) -> None:
|
||||
# Show how many events were covered by annotated lines above.
|
||||
printer = CcPrinter(events, annotated_ccs.ccs(), summary_cc)
|
||||
printer = CcPrinter(events, summary_cc)
|
||||
printer.init_ccs(annotated_ccs.ccs())
|
||||
print_fancy("Annotation summary")
|
||||
printer.print_events("")
|
||||
print()
|
||||
|
||||
total_cc = events.mk_empty_cc()
|
||||
for (cc, label) in zip(annotated_ccs.ccs(), AnnotatedCcs.labels):
|
||||
printer.print_cc(cc, label)
|
||||
total_cc += cc
|
||||
printer.print_cc(cc, None, label)
|
||||
add_cc_to_cc(cc, total_cc)
|
||||
|
||||
print()
|
||||
|
||||
@@ -826,19 +1003,19 @@ def main() -> None:
|
||||
desc,
|
||||
cmd,
|
||||
events,
|
||||
dict_flfn_cc,
|
||||
dict_fl_dcc,
|
||||
dict_fn_dcc,
|
||||
dict_fl_dict_line_cc,
|
||||
summary_cc,
|
||||
) = read_cgout_file()
|
||||
|
||||
# Each of the following calls prints a section of the output.
|
||||
|
||||
print_cachegrind_profile(desc, cmd, events)
|
||||
|
||||
print_metadata(desc, cmd, events)
|
||||
print_summary(events, summary_cc)
|
||||
|
||||
ann_src_filenames = print_function_summary(events, dict_flfn_cc, summary_cc)
|
||||
|
||||
ann_src_filenames = print_name_summary(
|
||||
"File:function", events, dict_fl_dcc, summary_cc
|
||||
)
|
||||
print_name_summary("Function:file", events, dict_fn_dcc, summary_cc)
|
||||
if args.annotate:
|
||||
annotated_ccs = print_annotated_src_files(
|
||||
events, ann_src_filenames, dict_fl_dict_line_cc, summary_cc
|
||||
|
||||
@@ -26,10 +26,8 @@
|
||||
#
|
||||
# The GNU General Public License is contained in the file COPYING.
|
||||
|
||||
"""
|
||||
This script diffs Cachegrind output files.
|
||||
"""
|
||||
|
||||
# This script diffs Cachegrind output files.
|
||||
#
|
||||
# Use `make pydiff` to "build" this script every time it is changed. This runs
|
||||
# the formatters, type-checkers, and linters on `cg_diff.in` and then generates
|
||||
# `cg_diff`.
|
||||
@@ -47,13 +45,9 @@ from typing import Callable, DefaultDict, NewType, NoReturn
|
||||
SearchAndReplace = Callable[[str], str]
|
||||
|
||||
|
||||
# A typed wrapper for parsed args.
|
||||
class Args(Namespace):
|
||||
"""
|
||||
A typed wrapper for parsed args.
|
||||
|
||||
None of these fields are modified after arg parsing finishes.
|
||||
"""
|
||||
|
||||
# None of these fields are modified after arg parsing finishes.
|
||||
mod_filename: SearchAndReplace
|
||||
mod_funcname: SearchAndReplace
|
||||
cgout_filename1: str
|
||||
@@ -146,10 +140,10 @@ class Events:
|
||||
self.events = text.split()
|
||||
self.num_events = len(self.events)
|
||||
|
||||
def mk_cc(self, text: str) -> Cc:
|
||||
"""Raises a `ValueError` exception on syntax error."""
|
||||
# Raises a `ValueError` exception on syntax error.
|
||||
def mk_cc(self, str_counts: list[str]) -> Cc:
|
||||
# This is slightly faster than a list comprehension.
|
||||
counts = list(map(int, text.split()))
|
||||
counts = list(map(int, str_counts))
|
||||
|
||||
if len(counts) == self.num_events:
|
||||
pass
|
||||
@@ -159,46 +153,31 @@ class Events:
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
return Cc(counts)
|
||||
return counts
|
||||
|
||||
def mk_empty_cc(self) -> Cc:
|
||||
# This is much faster than a list comprehension.
|
||||
return Cc([0] * self.num_events)
|
||||
return [0] * self.num_events
|
||||
|
||||
|
||||
class Cc:
|
||||
"""
|
||||
This is a dumb container for counts.
|
||||
# A "cost centre", which is a dumb container for counts. Always the same length
|
||||
# as `Events.events`, but it doesn't even know event names. `Events.mk_cc` and
|
||||
# `Events.mk_empty_cc` are used for construction.
|
||||
#
|
||||
# This used to be a class with a single field `counts: list[int]`, but this
|
||||
# type is very hot and just using a type alias is much faster.
|
||||
Cc = list[int]
|
||||
|
||||
It doesn't know anything about events, i.e. what each count means. It can
|
||||
do basic operations like `__iadd__` and `__eq__`, and anything more must be
|
||||
done elsewhere. `Events.mk_cc` and `Events.mk_empty_cc` are used for
|
||||
construction.
|
||||
"""
|
||||
# Add the counts in `a_cc` to `b_cc`.
|
||||
def add_cc_to_cc(a_cc: Cc, b_cc: Cc) -> None:
|
||||
for i, a_count in enumerate(a_cc):
|
||||
b_cc[i] += a_count
|
||||
|
||||
# Always the same length as `Events.events`.
|
||||
counts: list[int]
|
||||
|
||||
def __init__(self, counts: list[int]) -> None:
|
||||
self.counts = counts
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return str(self.counts)
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if not isinstance(other, Cc):
|
||||
return NotImplemented
|
||||
return self.counts == other.counts
|
||||
|
||||
def __iadd__(self, other: Cc) -> Cc:
|
||||
for i, other_count in enumerate(other.counts):
|
||||
self.counts[i] += other_count
|
||||
return self
|
||||
|
||||
def __isub__(self, other: Cc) -> Cc:
|
||||
for i, other_count in enumerate(other.counts):
|
||||
self.counts[i] -= other_count
|
||||
return self
|
||||
# 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
|
||||
|
||||
|
||||
# A paired filename and function name.
|
||||
@@ -252,33 +231,28 @@ def read_cgout_file(cgout_filename: str) -> tuple[str, Events, DictFlfnCc, Cc]:
|
||||
else:
|
||||
parse_die("missing an `events:` line")
|
||||
|
||||
curr_fl = ""
|
||||
curr_flfn = Flfn(("", ""))
|
||||
fl = ""
|
||||
flfn = Flfn(("", ""))
|
||||
|
||||
# Different places where we accumulate CC data.
|
||||
dict_flfn_cc: DictFlfnCc = defaultdict(events.mk_empty_cc)
|
||||
summary_cc = None
|
||||
|
||||
# Compile the one hot regex.
|
||||
count_pat = re.compile(r"(\d+)\s+(.*)")
|
||||
|
||||
# Line matching is done in order of pattern frequency, for speed.
|
||||
while True:
|
||||
line = readline()
|
||||
|
||||
if m := count_pat.match(line):
|
||||
# The line_num isn't used.
|
||||
while line := readline():
|
||||
if line[0].isdigit():
|
||||
split_line = line.split()
|
||||
try:
|
||||
cc = events.mk_cc(m.group(2))
|
||||
# The line_num isn't used.
|
||||
cc = events.mk_cc(split_line[1:])
|
||||
except ValueError:
|
||||
parse_die("malformed or too many event counts")
|
||||
|
||||
# Record this CC at the function level.
|
||||
flfn_cc = dict_flfn_cc[curr_flfn]
|
||||
flfn_cc += cc
|
||||
add_cc_to_cc(cc, dict_flfn_cc[flfn])
|
||||
|
||||
elif line.startswith("fn="):
|
||||
curr_flfn = Flfn((curr_fl, args.mod_funcname(line[3:-1])))
|
||||
flfn = Flfn((fl, args.mod_funcname(line[3:-1])))
|
||||
|
||||
elif line.startswith("fl="):
|
||||
# A longstanding bug: the use of `--mod-filename` makes it
|
||||
@@ -287,18 +261,15 @@ def read_cgout_file(cgout_filename: str) -> tuple[str, Events, DictFlfnCc, Cc]:
|
||||
# diffs anyway. It just means we get "This file was unreadable"
|
||||
# for modified filenames rather than a single "<unknown (line
|
||||
# 0)>" CC.
|
||||
curr_fl = args.mod_filename(line[3:-1])
|
||||
fl = args.mod_filename(line[3:-1])
|
||||
# A `fn=` line should follow, overwriting the "???".
|
||||
curr_flfn = Flfn((curr_fl, "???"))
|
||||
flfn = Flfn((fl, "???"))
|
||||
|
||||
elif m := re.match(r"summary:\s+(.*)", line):
|
||||
try:
|
||||
summary_cc = events.mk_cc(m.group(1))
|
||||
summary_cc = events.mk_cc(m.group(1).split())
|
||||
except ValueError:
|
||||
parse_die("too many event counts")
|
||||
|
||||
elif line == "":
|
||||
break # EOF
|
||||
parse_die("malformed or too many event counts")
|
||||
|
||||
elif line == "\n" or line.startswith("#"):
|
||||
# Skip empty lines and comment lines.
|
||||
@@ -314,7 +285,7 @@ def read_cgout_file(cgout_filename: str) -> tuple[str, Events, DictFlfnCc, Cc]:
|
||||
# Check summary is correct.
|
||||
total_cc = events.mk_empty_cc()
|
||||
for flfn_cc in dict_flfn_cc.values():
|
||||
total_cc += flfn_cc
|
||||
add_cc_to_cc(flfn_cc, total_cc)
|
||||
if summary_cc != total_cc:
|
||||
msg = (
|
||||
"`summary:` line doesn't match computed total\n"
|
||||
@@ -339,8 +310,8 @@ def main() -> None:
|
||||
# Subtract file 1's CCs from file 2's CCs, at the Flfn level.
|
||||
for flfn, flfn_cc1 in dict_flfn_cc1.items():
|
||||
flfn_cc2 = dict_flfn_cc2[flfn]
|
||||
flfn_cc2 -= flfn_cc1
|
||||
summary_cc2 -= summary_cc1
|
||||
sub_cc_from_cc(flfn_cc1, flfn_cc2)
|
||||
sub_cc_from_cc(summary_cc1, summary_cc2)
|
||||
|
||||
print(f"desc: Files compared: {filename1}; {filename2}")
|
||||
print(f"cmd: {cmd1}; {cmd2}")
|
||||
@@ -356,9 +327,9 @@ def main() -> None:
|
||||
# move around.
|
||||
print(f"fl={flfn[0]}")
|
||||
print(f"fn={flfn[1]}")
|
||||
print("0", *flfn_cc2.counts, sep=" ")
|
||||
print("0", *flfn_cc2, sep=" ")
|
||||
|
||||
print("summary:", *summary_cc2.counts, sep=" ")
|
||||
print("summary:", *summary_cc2, sep=" ")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -26,10 +26,8 @@
|
||||
#
|
||||
# The GNU General Public License is contained in the file COPYING.
|
||||
|
||||
"""
|
||||
This script diffs Cachegrind output files.
|
||||
"""
|
||||
|
||||
# This script merges Cachegrind output files.
|
||||
#
|
||||
# Use `make pymerge` to "build" this script every time it is changed. This runs
|
||||
# the formatters, type-checkers, and linters on `cg_merge.in` and then
|
||||
# generates `cg_merge`.
|
||||
@@ -45,13 +43,9 @@ from collections import defaultdict
|
||||
from typing import DefaultDict, NoReturn, TextIO
|
||||
|
||||
|
||||
# A typed wrapper for parsed args.
|
||||
class Args(Namespace):
|
||||
"""
|
||||
A typed wrapper for parsed args.
|
||||
|
||||
None of these fields are modified after arg parsing finishes.
|
||||
"""
|
||||
|
||||
# None of these fields are modified after arg parsing finishes.
|
||||
output: str
|
||||
cgout_filename: list[str]
|
||||
|
||||
@@ -92,10 +86,10 @@ class Events:
|
||||
self.events = text.split()
|
||||
self.num_events = len(self.events)
|
||||
|
||||
def mk_cc(self, text: str) -> Cc:
|
||||
"""Raises a `ValueError` exception on syntax error."""
|
||||
# Raises a `ValueError` exception on syntax error.
|
||||
def mk_cc(self, str_counts: list[str]) -> Cc:
|
||||
# This is slightly faster than a list comprehension.
|
||||
counts = list(map(int, text.split()))
|
||||
counts = list(map(int, str_counts))
|
||||
|
||||
if len(counts) == self.num_events:
|
||||
pass
|
||||
@@ -105,41 +99,26 @@ class Events:
|
||||
else:
|
||||
raise ValueError
|
||||
|
||||
return Cc(counts)
|
||||
return counts
|
||||
|
||||
def mk_empty_cc(self) -> Cc:
|
||||
# This is much faster than a list comprehension.
|
||||
return Cc([0] * self.num_events)
|
||||
return [0] * self.num_events
|
||||
|
||||
|
||||
class Cc:
|
||||
"""
|
||||
This is a dumb container for counts.
|
||||
# A "cost centre", which is a dumb container for counts. Always the same length
|
||||
# as `Events.events`, but it doesn't even know event names. `Events.mk_cc` and
|
||||
# `Events.mk_empty_cc` are used for construction.
|
||||
#
|
||||
# This used to be a class with a single field `counts: list[int]`, but this
|
||||
# type is very hot and just using a type alias is much faster.
|
||||
Cc = list[int]
|
||||
|
||||
It doesn't know anything about events, i.e. what each count means. It can
|
||||
do basic operations like `__iadd__` and `__eq__`, and anything more must be
|
||||
done elsewhere. `Events.mk_cc` and `Events.mk_empty_cc` are used for
|
||||
construction.
|
||||
"""
|
||||
|
||||
# Always the same length as `Events.events`.
|
||||
counts: list[int]
|
||||
|
||||
def __init__(self, counts: list[int]) -> None:
|
||||
self.counts = counts
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return str(self.counts)
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if not isinstance(other, Cc):
|
||||
return NotImplemented
|
||||
return self.counts == other.counts
|
||||
|
||||
def __iadd__(self, other: Cc) -> Cc:
|
||||
for i, other_count in enumerate(other.counts):
|
||||
self.counts[i] += other_count
|
||||
return self
|
||||
# Add the counts in `a_cc` to `b_cc`.
|
||||
def add_cc_to_cc(a_cc: Cc, b_cc: Cc) -> None:
|
||||
for i, a_count in enumerate(a_cc):
|
||||
b_cc[i] += a_count
|
||||
|
||||
|
||||
# Per-line CCs, organised by filename, function name, and line number.
|
||||
@@ -205,8 +184,8 @@ def read_cgout_file(
|
||||
|
||||
summary_cc_present = False
|
||||
|
||||
curr_fl = ""
|
||||
curr_fn = ""
|
||||
fl = ""
|
||||
fn = ""
|
||||
|
||||
# The `cumul_*` values are passed in by reference and are modified by
|
||||
# this function. But they can't be properly initialized until the
|
||||
@@ -217,43 +196,35 @@ def read_cgout_file(
|
||||
cumul_dict_fl_dict_fn_dict_line_cc.default_factory = (
|
||||
mk_empty_dict_fn_dict_line_cc
|
||||
)
|
||||
cumul_summary_cc.counts = events.mk_empty_cc().counts
|
||||
|
||||
# Compile the one hot regex.
|
||||
count_pat = re.compile(r"(\d+)\s+(.*)")
|
||||
cumul_summary_cc.extend(events.mk_empty_cc())
|
||||
|
||||
# Line matching is done in order of pattern frequency, for speed.
|
||||
while True:
|
||||
line = readline()
|
||||
|
||||
if m := count_pat.match(line):
|
||||
line_num = int(m.group(1))
|
||||
while line := readline():
|
||||
if line[0].isdigit():
|
||||
split_line = line.split()
|
||||
try:
|
||||
cc = events.mk_cc(m.group(2))
|
||||
line_num = int(split_line[0])
|
||||
cc = events.mk_cc(split_line[1:])
|
||||
except ValueError:
|
||||
parse_die("malformed or too many event counts")
|
||||
|
||||
# Record this CC at the file/func/line level.
|
||||
line_cc = cumul_dict_fl_dict_fn_dict_line_cc[curr_fl][curr_fn][line_num]
|
||||
line_cc += cc
|
||||
add_cc_to_cc(cc, cumul_dict_fl_dict_fn_dict_line_cc[fl][fn][line_num])
|
||||
|
||||
elif line.startswith("fn="):
|
||||
curr_fn = line[3:-1]
|
||||
fn = line[3:-1]
|
||||
|
||||
elif line.startswith("fl="):
|
||||
curr_fl = line[3:-1]
|
||||
fl = line[3:-1]
|
||||
# A `fn=` line should follow, overwriting the "???".
|
||||
curr_fn = "???"
|
||||
fn = "???"
|
||||
|
||||
elif m := re.match(r"summary:\s+(.*)", line):
|
||||
summary_cc_present = True
|
||||
try:
|
||||
cumul_summary_cc += events.mk_cc(m.group(1))
|
||||
add_cc_to_cc(events.mk_cc(m.group(1).split()), cumul_summary_cc)
|
||||
except ValueError:
|
||||
parse_die("too many event counts")
|
||||
|
||||
elif line == "":
|
||||
break # EOF
|
||||
parse_die("malformed or too many event counts")
|
||||
|
||||
elif line == "\n" or line.startswith("#"):
|
||||
# Skip empty lines and comment lines.
|
||||
@@ -283,7 +254,7 @@ def main() -> None:
|
||||
# Different places where we accumulate CC data. Initialized to invalid
|
||||
# states prior to the number of events being known.
|
||||
cumul_dict_fl_dict_fn_dict_line_cc: DictFlDictFnDictLineCc = defaultdict(None)
|
||||
cumul_summary_cc: Cc = Cc([])
|
||||
cumul_summary_cc: Cc = []
|
||||
|
||||
for n, filename in enumerate(args.cgout_filename):
|
||||
is_first_file = n == 0
|
||||
@@ -321,9 +292,9 @@ def main() -> None:
|
||||
for fn, dict_line_cc in dict_fn_dict_line_cc.items():
|
||||
print(f"fn={fn}", file=f)
|
||||
for line, cc in dict_line_cc.items():
|
||||
print(line, *cc.counts, file=f)
|
||||
print(line, *cc, file=f)
|
||||
|
||||
print("summary:", *cumul_summary_cc.counts, sep=" ", file=f)
|
||||
print("summary:", *cumul_summary_cc, sep=" ", file=f)
|
||||
|
||||
if args.output:
|
||||
try:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
--------------------------------------------------------------------------------
|
||||
-- Cachegrind profile
|
||||
-- Metadata
|
||||
--------------------------------------------------------------------------------
|
||||
Files compared: ann1.cgout; ann1b.cgout
|
||||
Command: ./a.out; ./a.out
|
||||
@@ -14,28 +14,35 @@ Annotation: on
|
||||
--------------------------------------------------------------------------------
|
||||
-- Summary
|
||||
--------------------------------------------------------------------------------
|
||||
Ir I1mr ILmr Dr D1mr DLmr Dw D1mw DLmw
|
||||
Ir________________ I1mr ILmr Dr_________________ D1mr DLmr Dw D1mw DLmw
|
||||
|
||||
5,000,000 (100.0%) 0 0 -2,000,000 (100.0%) 0 0 0 0 0 PROGRAM TOTALS
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Function summary
|
||||
-- File:function summary
|
||||
--------------------------------------------------------------------------------
|
||||
Ir I1mr ILmr Dr D1mr DLmr Dw D1mw DLmw file:function
|
||||
Ir________________________ I1mr________ ILmr________ Dr_________________________ D1mr________ DLmr________ Dw__________ D1mw________ DLmw________ file:function
|
||||
|
||||
5,000,000 (100.0%) 0 0 -2,000,000 (100.0%) 0 0 0 0 0 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
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Annotated source file: a.c
|
||||
--------------------------------------------------------------------------------
|
||||
Ir I1mr ILmr Dr D1mr DLmr Dw D1mw DLmw
|
||||
Ir________________ I1mr ILmr Dr_________________ D1mr DLmr Dw D1mw DLmw
|
||||
|
||||
5,000,000 (100.0%) 0 0 -2,000,000 (100.0%) 0 0 0 0 0 <unknown (line 0)>
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Annotation summary
|
||||
--------------------------------------------------------------------------------
|
||||
Ir I1mr ILmr Dr D1mr DLmr Dw D1mw DLmw
|
||||
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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
--------------------------------------------------------------------------------
|
||||
-- Cachegrind profile
|
||||
-- Metadata
|
||||
--------------------------------------------------------------------------------
|
||||
Files compared: ann-diff2a.cgout; ann-diff2b.cgout
|
||||
Command: cmd1; cmd2
|
||||
@@ -14,18 +14,30 @@ Annotation: on
|
||||
--------------------------------------------------------------------------------
|
||||
-- Summary
|
||||
--------------------------------------------------------------------------------
|
||||
One Two
|
||||
One___________ Two___________
|
||||
|
||||
2,100 (100.0%) 1,900 (100.0%) PROGRAM TOTALS
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Function summary
|
||||
-- File:function summary
|
||||
--------------------------------------------------------------------------------
|
||||
One Two file:function
|
||||
One___________________ Two___________________ file:function
|
||||
|
||||
1,000 (47.6%) 1,000 (52.6%) aux/ann-diff2-basic.rs:groffN
|
||||
1,000 (47.6%) 1,000 (52.6%) aux/ann-diff2-basic.rs:fN_ffN_fooN_F4_g5
|
||||
100 (4.8%) -100 (-5.3%) aux/ann-diff2-basic.rs:basic1
|
||||
> 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
|
||||
@@ -35,7 +47,7 @@ This file was unreadable
|
||||
--------------------------------------------------------------------------------
|
||||
-- Annotation summary
|
||||
--------------------------------------------------------------------------------
|
||||
One Two
|
||||
One___________ Two___________
|
||||
|
||||
0 0 annotated: files known & above threshold & readable, line numbers known
|
||||
0 0 annotated: files known & above threshold & readable, line numbers unknown
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
--------------------------------------------------------------------------------
|
||||
-- Cachegrind profile
|
||||
-- Metadata
|
||||
--------------------------------------------------------------------------------
|
||||
Description 1a
|
||||
Description 1b
|
||||
Command: Command 1
|
||||
Data file: ann-merge1c.cgout
|
||||
Events recorded: A B C
|
||||
Events shown: A B C
|
||||
Event sort order: A B C
|
||||
Events recorded: A
|
||||
Events shown: A
|
||||
Event sort order: A
|
||||
Threshold: 0.1
|
||||
Include dirs:
|
||||
Annotation: on
|
||||
@@ -15,51 +15,66 @@ Annotation: on
|
||||
--------------------------------------------------------------------------------
|
||||
-- Summary
|
||||
--------------------------------------------------------------------------------
|
||||
A B C
|
||||
A__________
|
||||
|
||||
86 (100.0%) 113 (100.0%) 145 (100.0%) PROGRAM TOTALS
|
||||
86 (100.0%) PROGRAM TOTALS
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Function summary
|
||||
-- File:function summary
|
||||
--------------------------------------------------------------------------------
|
||||
A B C file:function
|
||||
A_________________ file:function
|
||||
|
||||
40 (46.5%) 80 (70.8%) 120 (82.8%) ann-merge-x.rs:x1
|
||||
20 (23.3%) 10 (8.8%) 5 (3.4%) ann-merge-x.rs:x3
|
||||
16 (18.6%) 18 (15.9%) 20 (13.8%) ann-merge-y.rs:y1
|
||||
10 (11.6%) 5 (4.4%) 0 ann-merge-x.rs:x2
|
||||
> 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 B C
|
||||
A_________
|
||||
|
||||
20 (23.3%) 40 (35.4%) 60 (41.4%) one
|
||||
10 (11.6%) 20 (17.7%) 30 (20.7%) two
|
||||
10 (11.6%) 20 (17.7%) 30 (20.7%) three
|
||||
10 (11.6%) 5 (4.4%) 0 four
|
||||
20 (23.3%) 10 (8.8%) 5 (3.4%) five
|
||||
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 B C
|
||||
A_______
|
||||
|
||||
8 (9.3%) 9 (8.0%) 10 (6.9%) one
|
||||
8 (9.3%) 9 (8.0%) 10 (6.9%) two
|
||||
. . . three
|
||||
. . . four
|
||||
. . . five
|
||||
. . . six
|
||||
8 (9.3%) one
|
||||
8 (9.3%) two
|
||||
. three
|
||||
. four
|
||||
. five
|
||||
. six
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Annotation summary
|
||||
--------------------------------------------------------------------------------
|
||||
A B C
|
||||
A__________
|
||||
|
||||
86 (100.0%) 113 (100.0%) 145 (100.0%) 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 & unreadable
|
||||
0 0 0 unannotated: files known & below threshold
|
||||
0 0 0 unannotated: files unknown
|
||||
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 & unreadable
|
||||
0 unannotated: files known & below threshold
|
||||
0 unannotated: files unknown
|
||||
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
desc: Description 1a
|
||||
desc: Description 1b
|
||||
cmd: Command 1
|
||||
events: A B C
|
||||
events: A
|
||||
|
||||
fl=ann-merge-x.rs
|
||||
fn=x1
|
||||
1 10 20 30
|
||||
2 10 20 30
|
||||
1 10
|
||||
2 10
|
||||
|
||||
fn=x2
|
||||
4 10 5 0
|
||||
4 10
|
||||
|
||||
fl=ann-merge-y.rs
|
||||
fn=y1
|
||||
1 8 9 10
|
||||
2 8 9 10
|
||||
1 8
|
||||
2 8
|
||||
|
||||
summary: 46 63 80
|
||||
summary: 46
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
desc: Description 2a
|
||||
desc: Description 2b
|
||||
cmd: Command 2
|
||||
events: A B C
|
||||
events: A
|
||||
|
||||
fl=ann-merge-x.rs
|
||||
fn=x1
|
||||
1 10 20 30
|
||||
3 10 20 30
|
||||
1 10
|
||||
3 10
|
||||
|
||||
fn=x3
|
||||
5 20 10 5
|
||||
5 20
|
||||
|
||||
summary: 40 50 65
|
||||
summary: 40
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
--------------------------------------------------------------------------------
|
||||
-- Cachegrind profile
|
||||
-- Metadata
|
||||
--------------------------------------------------------------------------------
|
||||
I1 cache: 32768 B, 64 B, 8-way associative
|
||||
D1 cache: 32768 B, 64 B, 8-way associative
|
||||
@@ -16,24 +16,61 @@ Annotation: on
|
||||
--------------------------------------------------------------------------------
|
||||
-- Summary
|
||||
--------------------------------------------------------------------------------
|
||||
Ir I1mr ILmr
|
||||
Ir_______ I1mr ILmr
|
||||
|
||||
5,229,753 952 931 PROGRAM TOTALS
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Function summary
|
||||
-- File:function summary
|
||||
--------------------------------------------------------------------------------
|
||||
Ir I1mr ILmr file:function
|
||||
Ir_______ I1mr ILmr file:function
|
||||
|
||||
5,000,015 1 1 a.c:main
|
||||
47,993 19 19 /build/glibc-OTsEL5/glibc-2.27/elf/dl-lookup.c:do_lookup_x
|
||||
28,534 11 11 /build/glibc-OTsEL5/glibc-2.27/elf/dl-lookup.c:_dl_lookup_symbol_x
|
||||
28,136 7 7 /build/glibc-OTsEL5/glibc-2.27/elf/dl-tunables.c:__GI___tunables_init
|
||||
25,408 47 47 /build/glibc-OTsEL5/glibc-2.27/string/../sysdeps/x86_64/strcmp.S:strcmp
|
||||
21,821 23 23 /build/glibc-OTsEL5/glibc-2.27/elf/../sysdeps/x86_64/dl-machine.h:_dl_relocate_object
|
||||
11,521 15 15 /build/glibc-OTsEL5/glibc-2.27/elf/do-rel.h:_dl_relocate_object
|
||||
8,055 0 0 /build/glibc-OTsEL5/glibc-2.27/elf/dl-tunables.h:__GI___tunables_init
|
||||
6,898 2 2 /build/glibc-OTsEL5/glibc-2.27/elf/dl-misc.c:_dl_name_match_p
|
||||
> 5,000,015 1 1 a.c:main
|
||||
|
||||
> 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,136 7 7 __GI___tunables_init
|
||||
|
||||
> 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:
|
||||
21,821 23 23 _dl_relocate_object
|
||||
|
||||
> 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
|
||||
|
||||
> 6,939 5 5 /build/glibc-OTsEL5/glibc-2.27/elf/dl-misc.c:
|
||||
6,898 2 2 _dl_name_match_p
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Function:file summary
|
||||
--------------------------------------------------------------------------------
|
||||
Ir_______ I1mr ILmr function:file
|
||||
|
||||
> 5,000,015 1 1 main:a.c
|
||||
|
||||
> 48,347 20 20 do_lookup_x:
|
||||
47,993 19 19 /build/glibc-OTsEL5/glibc-2.27/elf/dl-lookup.c
|
||||
|
||||
> 36,191 7 7 __GI___tunables_init:
|
||||
28,136 7 7 /build/glibc-OTsEL5/glibc-2.27/elf/dl-tunables.c
|
||||
8,055 0 0 /build/glibc-OTsEL5/glibc-2.27/elf/dl-tunables.h
|
||||
|
||||
> 34,576 51 51 _dl_relocate_object:
|
||||
21,821 23 23 /build/glibc-OTsEL5/glibc-2.27/elf/../sysdeps/x86_64/dl-machine.h
|
||||
11,521 15 15 /build/glibc-OTsEL5/glibc-2.27/elf/do-rel.h
|
||||
|
||||
> 28,534 11 11 _dl_lookup_symbol_x:/build/glibc-OTsEL5/glibc-2.27/elf/dl-lookup.c
|
||||
|
||||
> 25,426 48 48 strcmp:
|
||||
25,408 47 47 /build/glibc-OTsEL5/glibc-2.27/string/../sysdeps/x86_64/strcmp.S
|
||||
|
||||
> 6,898 2 2 _dl_name_match_p:/build/glibc-OTsEL5/glibc-2.27/elf/dl-misc.c
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Annotated source file: /build/glibc-OTsEL5/glibc-2.27/elf/../sysdeps/x86_64/dl-machine.h
|
||||
@@ -73,7 +110,7 @@ This file was unreadable
|
||||
--------------------------------------------------------------------------------
|
||||
-- Annotated source file: a.c
|
||||
--------------------------------------------------------------------------------
|
||||
Ir I1mr ILmr
|
||||
Ir_______ I1mr ILmr
|
||||
|
||||
2 0 0 int main(void) {
|
||||
1 1 1 int z = 0;
|
||||
@@ -86,7 +123,7 @@ Ir I1mr ILmr
|
||||
--------------------------------------------------------------------------------
|
||||
-- Annotation summary
|
||||
--------------------------------------------------------------------------------
|
||||
Ir I1mr ILmr
|
||||
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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
--------------------------------------------------------------------------------
|
||||
-- Cachegrind profile
|
||||
-- Metadata
|
||||
--------------------------------------------------------------------------------
|
||||
I1 cache: 32768 B, 64 B, 8-way associative
|
||||
D1 cache: 32768 B, 64 B, 8-way associative
|
||||
@@ -16,19 +16,47 @@ Annotation: off
|
||||
--------------------------------------------------------------------------------
|
||||
-- Summary
|
||||
--------------------------------------------------------------------------------
|
||||
Dw Dr Ir
|
||||
Dw_____________ Dr________________ Ir________________
|
||||
|
||||
18,005 (100.0%) 4,057,955 (100.0%) 5,229,753 (100.0%) PROGRAM TOTALS
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Function summary
|
||||
-- File:function summary
|
||||
--------------------------------------------------------------------------------
|
||||
Dw Dr Ir file:function
|
||||
Dw__________________ Dr______________________ Ir______________________ file:function
|
||||
|
||||
3 (0.0%) 4,000,004 (98.6%) 5,000,015 (95.6%) a.c:main
|
||||
4,543 (25.2%) 17,566 (0.4%) 47,993 (0.9%) /build/glibc-OTsEL5/glibc-2.27/elf/dl-lookup.c:do_lookup_x
|
||||
3,083 (17.1%) 5,750 (0.1%) 28,534 (0.5%) /build/glibc-OTsEL5/glibc-2.27/elf/dl-lookup.c:_dl_lookup_symbol_x
|
||||
8 (0.0%) 5,521 (0.1%) 28,136 (0.5%) /build/glibc-OTsEL5/glibc-2.27/elf/dl-tunables.c:__GI___tunables_init
|
||||
2,490 (13.8%) 5,219 (0.1%) 21,821 (0.4%) /build/glibc-OTsEL5/glibc-2.27/elf/../sysdeps/x86_64/dl-machine.h:_dl_relocate_object
|
||||
0 5,158 (0.1%) 25,408 (0.5%) /build/glibc-OTsEL5/glibc-2.27/string/../sysdeps/x86_64/strcmp.S:strcmp
|
||||
> 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:
|
||||
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:
|
||||
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,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
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Function:file summary
|
||||
--------------------------------------------------------------------------------
|
||||
Dw__________________ Dr______________________ Ir______________________ function:file
|
||||
|
||||
> 3 (0.0%, 0.0%) 4,000,004 (98.6%, 98.6%) 5,000,015 (95.6%, 95.6%) main:a.c
|
||||
|
||||
> 4,543 (25.2%, 25.2%) 17,684 (0.4%, 99.0%) 48,347 (0.9%, 96.5%) do_lookup_x:
|
||||
4,543 (25.2%) 17,566 (0.4%) 47,993 (0.9%) /build/glibc-OTsEL5/glibc-2.27/elf/dl-lookup.c
|
||||
|
||||
> 3,010 (16.7%, 42.0%) 8,480 (0.2%, 99.2%) 34,576 (0.7%, 97.2%) _dl_relocate_object:
|
||||
2,490 (13.8%) 5,219 (0.1%) 21,821 (0.4%) /build/glibc-OTsEL5/glibc-2.27/elf/../sysdeps/x86_64/dl-machine.h
|
||||
|
||||
> 8 (0.0%, 42.0%) 7,430 (0.2%, 99.4%) 36,191 (0.7%, 97.9%) __GI___tunables_init:
|
||||
8 (0.0%) 5,521 (0.1%) 28,136 (0.5%) /build/glibc-OTsEL5/glibc-2.27/elf/dl-tunables.c
|
||||
|
||||
> 3,083 (17.1%, 59.1%) 5,750 (0.1%, 99.5%) 28,534 (0.5%, 98.4%) _dl_lookup_symbol_x:/build/glibc-OTsEL5/glibc-2.27/elf/dl-lookup.c
|
||||
|
||||
> 0 (0.0%, 59.1%) 5,166 (0.1%, 99.7%) 25,426 (0.5%, 98.9%) strcmp:
|
||||
0 5,158 (0.1%) 25,408 (0.5%) /build/glibc-OTsEL5/glibc-2.27/string/../sysdeps/x86_64/strcmp.S
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ events: A SomeCount VeryLongEventName
|
||||
fl=ann2-basic.rs
|
||||
# This one has the counts to get the totals to 100,000/100,000/0.
|
||||
fn=f0
|
||||
7 70091 90291 0
|
||||
7 68081 90291 0
|
||||
fn=f1
|
||||
# Different whitespace. Mix of line 0 and other lines.
|
||||
0 5000 0 0
|
||||
@@ -45,24 +45,29 @@ fl=ann2-more-recent-than-cgout.rs
|
||||
fn=new
|
||||
2 1000 0 0
|
||||
|
||||
# File with negative and positive values.
|
||||
# File with negative and positive values. Some values are very large (much
|
||||
# bigger than in the summary). The file total is below the threshold, but it
|
||||
# still is printed because the function CCs are above the threshold. Also,
|
||||
# because the summary value for `ThisIsAVeryLongEventName` is zero, that events
|
||||
# percentages here show up as "n/a".
|
||||
fl=ann2-negatives.rs
|
||||
# Various, and the sum is zero.
|
||||
fn=neg1
|
||||
0 -1000 -1000 -1000
|
||||
1 2000 2000 2000
|
||||
2 -1000 -1000 0
|
||||
fn=neg2
|
||||
# Enormous numbers, but the sum is zero or almost zero.
|
||||
# Also, because the summary value for `ThisIsAVeryLongEventName` is zero, the
|
||||
# percentages here show up as zero.
|
||||
5 999000 0 -150000
|
||||
6 -1000000 0 150000
|
||||
fn=neg2a
|
||||
5 500000 0 -150000
|
||||
fn=neg2b
|
||||
6 499999 0 0
|
||||
fn=neg3
|
||||
# Ditto.
|
||||
0 -1000 0 10
|
||||
10 -10000 0 10
|
||||
11 10000 0 -20
|
||||
8 -1000000 0 150000
|
||||
10 -8000 0 10
|
||||
11 9000 0 -20
|
||||
fn=neg4
|
||||
13 11 0 0
|
||||
|
||||
# File with source newer than the cgout file.
|
||||
fl=ann2-past-the-end.rs
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
--------------------------------------------------------------------------------
|
||||
-- Cachegrind profile
|
||||
-- Metadata
|
||||
--------------------------------------------------------------------------------
|
||||
Command: ann2
|
||||
Data file: ann2.cgout
|
||||
@@ -15,55 +15,94 @@ Annotation: on
|
||||
--------------------------------------------------------------------------------
|
||||
-- Summary
|
||||
--------------------------------------------------------------------------------
|
||||
A SomeCount VeryLongEventName
|
||||
A_______________ SomeCount_______ VeryLongEventName
|
||||
|
||||
100,000 (100.0%) 100,000 (100.0%) 0 PROGRAM TOTALS
|
||||
100,000 (100.0%) 100,000 (100.0%) 0 PROGRAM TOTALS
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Function summary
|
||||
-- File:function summary
|
||||
--------------------------------------------------------------------------------
|
||||
A SomeCount VeryLongEventName file:function
|
||||
A___________________________ SomeCount____________ VeryLongEventName__ file:function
|
||||
|
||||
70,091 (70.1%) 90,291 (90.3%) 0 ann2-basic.rs:f0
|
||||
15,000 (15.0%) 600 (0.6%) 0 ann2-basic.rs:f1
|
||||
9,000 (9.0%) 6,000 (6.0%) 0 ann2-could-not-be-found.rs:f1
|
||||
2,000 (2.0%) 100 (0.1%) 0 ann2-basic.rs:f2
|
||||
1,000 (1.0%) 500 (0.5%) 0 ann2-via-I.rs:<unspecified>
|
||||
1,000 (1.0%) 300 (0.3%) -1,000 (n/a) ann2-past-the-end.rs:f1
|
||||
-1,000 (-1.0%) 0 0 ann2-negatives.rs:neg3
|
||||
-1,000 (-1.0%) 0 0 ann2-negatives.rs:neg2
|
||||
1,000 (1.0%) 0 0 ann2-more-recent-than-cgout.rs:new
|
||||
1,000 (1.0%) 0 0 ???:unknown
|
||||
500 (0.5%) 0 0 ann2-basic.rs:f6
|
||||
500 (0.5%) 0 0 ann2-basic.rs:f4
|
||||
> 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
|
||||
|
||||
> 1,000 (1.0%, 96.6%) 500 (0.5%, 99.5%) 0 (n/a, n/a) ann2-via-I.rs:<unspecified>
|
||||
|
||||
> 1,000 (1.0%, 97.6%) 300 (0.3%, 99.8%) -1,000 (n/a, n/a) ann2-past-the-end.rs: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%, 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:
|
||||
-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
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Function:file summary
|
||||
--------------------------------------------------------------------------------
|
||||
A______________________________ SomeCount____________ VeryLongEventName__ function:file
|
||||
|
||||
> -1,000,000 (-1000.0%, -1000.0%) 0 (0.0%, 0.0%) 150,000 (n/a, n/a) neg3:ann2-negatives.rs
|
||||
|
||||
> 500,000 (500.0%, -500.0%) 0 (0.0%, 0.0%) -150,000 (n/a, n/a) neg2a:ann2-negatives.rs
|
||||
|
||||
> 499,999 (500.0%, -0.0%) 0 (0.0%, 0.0%) 0 (n/a, n/a) neg2b:ann2-negatives.rs
|
||||
|
||||
> 68,081 (68.1%, 68.1%) 90,291 (90.3%, 90.3%) 0 (n/a, n/a) f0:ann2-basic.rs
|
||||
|
||||
> 25,000 (25.0%, 93.1%) 6,900 (6.9%, 97.2%) -1,000 (n/a, n/a) f1:
|
||||
15,000 (15.0%) 600 (0.6%) 0 ann2-basic.rs
|
||||
9,000 (9.0%) 6,000 (6.0%) 0 ann2-could-not-be-found.rs
|
||||
1,000 (1.0%) 300 (0.3%) -1,000 (n/a) ann2-past-the-end.rs
|
||||
|
||||
> 2,000 (2.0%, 95.1%) 100 (0.1%, 97.3%) 0 (n/a, n/a) f2:ann2-basic.rs
|
||||
|
||||
> 1,000 (1.0%, 96.1%) 500 (0.5%, 97.8%) 0 (n/a, n/a) <unspecified>:ann2-via-I.rs
|
||||
|
||||
> 1,000 (1.0%, 97.1%) 0 (0.0%, 97.8%) 0 (n/a, n/a) unknown:???
|
||||
|
||||
> 1,000 (1.0%, 98.1%) 0 (0.0%, 97.8%) 0 (n/a, n/a) new:ann2-more-recent-than-cgout.rs
|
||||
|
||||
> 500 (0.5%, 98.6%) 0 (0.0%, 97.8%) 0 (n/a, n/a) f6:ann2-basic.rs
|
||||
|
||||
> 500 (0.5%, 99.1%) 0 (0.0%, 97.8%) 0 (n/a, n/a) f4:ann2-basic.rs
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Annotated source file: ann2-basic.rs
|
||||
--------------------------------------------------------------------------------
|
||||
A SomeCount VeryLongEventName
|
||||
A_____________ SomeCount_____ VeryLongEventName
|
||||
|
||||
7,100 (7.1%) 100 (0.1%) 0 <unknown (line 0)>
|
||||
7,100 (7.1%) 100 (0.1%) 0 <unknown (line 0)>
|
||||
|
||||
-- line 2 ----------------------------------------
|
||||
. . . two
|
||||
. . . three
|
||||
5,000 (5.0%) 500 (0.5%) 0 four
|
||||
5,000 (5.0%) 100 (0.1%) 0 five
|
||||
. . . six
|
||||
70,091 (70.1%) 90,291 (90.3%) 0 seven
|
||||
. . . eight
|
||||
110 (0.1%) 9 (0.0%) 0 nine
|
||||
. . . ten
|
||||
. . . eleven
|
||||
200 (0.2%) 0 0 twelve
|
||||
200 (0.2%) 0 0 thirteen
|
||||
100 (0.1%) 0 0 fourteen
|
||||
0 0 0 fifteen
|
||||
0 0 0 sixteen
|
||||
0 0 0 seventeen
|
||||
0 0 0 eighteen
|
||||
499 (0.5%) 2,000 (2.0%) 0 nineteen
|
||||
300 (0.3%) 0 0 twenty
|
||||
. . . two
|
||||
. . . three
|
||||
5,000 (5.0%) 500 (0.5%) 0 four
|
||||
5,000 (5.0%) 100 (0.1%) 0 five
|
||||
. . . six
|
||||
68,081 (68.1%) 90,291 (90.3%) 0 seven
|
||||
. . . eight
|
||||
110 (0.1%) 9 (0.0%) 0 nine
|
||||
. . . ten
|
||||
. . . eleven
|
||||
200 (0.2%) 0 0 twelve
|
||||
200 (0.2%) 0 0 thirteen
|
||||
100 (0.1%) 0 0 fourteen
|
||||
0 0 0 fifteen
|
||||
0 0 0 sixteen
|
||||
0 0 0 seventeen
|
||||
0 0 0 eighteen
|
||||
499 (0.5%) 2,000 (2.0%) 0 nineteen
|
||||
300 (0.3%) 0 0 twenty
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Annotated source file: ann2-could-not-be-found.rs
|
||||
@@ -80,7 +119,7 @@ This file was unreadable
|
||||
@ Annotations may not be correct.
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
|
||||
A SomeCount VeryLongEventName
|
||||
A___________ SomeCount VeryLongEventName
|
||||
|
||||
. . . one
|
||||
1,000 (1.0%) 0 0 two
|
||||
@@ -91,38 +130,40 @@ A SomeCount VeryLongEventName
|
||||
--------------------------------------------------------------------------------
|
||||
-- Annotated source file: ann2-negatives.rs
|
||||
--------------------------------------------------------------------------------
|
||||
A SomeCount VeryLongEventName
|
||||
A____________________ SomeCount_____ VeryLongEventName
|
||||
|
||||
-2,000 (-2.0%) -1,000 (-1.0%) -990 (n/a) <unknown (line 0)>
|
||||
-2,000 (-2.0%) -1,000 (-1.0%) -990 (n/a) <unknown (line 0)>
|
||||
|
||||
2,000 (2.0%) 2,000 (2.0%) 2,000 (n/a) one
|
||||
-1,000 (-1.0%) -1,000 (-1.0%) 0 two
|
||||
. . . three
|
||||
. . . four
|
||||
999,000 (999.0%) 0 -150,000 (n/a) five
|
||||
-1,000,000 (-1000.0%) 0 150,000 (n/a) six
|
||||
. . . seven
|
||||
. . . eight
|
||||
. . . nine
|
||||
-10,000 (-10.0%) 0 10 (n/a) ten
|
||||
10,000 (10.0%) 0 -20 (n/a) eleven
|
||||
. . . twelve
|
||||
. . . thirteen
|
||||
-- line 13 ----------------------------------------
|
||||
2,000 (2.0%) 2,000 (2.0%) 2,000 (n/a) one
|
||||
-1,000 (-1.0%) -1,000 (-1.0%) 0 two
|
||||
. . . three
|
||||
. . . four
|
||||
500,000 (500.0%) 0 -150,000 (n/a) five
|
||||
499,999 (500.0%) 0 0 six
|
||||
. . . seven
|
||||
-1,000,000 (-1000.0%) 0 150,000 (n/a) eight
|
||||
. . . nine
|
||||
-8,000 (-8.0%) 0 10 (n/a) ten
|
||||
9,000 (9.0%) 0 -20 (n/a) eleven
|
||||
. . . twelve
|
||||
11 (0.0%) 0 0 thirteen
|
||||
. . . fourteen
|
||||
. . . fifteen
|
||||
-- line 15 ----------------------------------------
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Annotated source file: ann2-past-the-end.rs
|
||||
--------------------------------------------------------------------------------
|
||||
A SomeCount VeryLongEventName
|
||||
A_________ SomeCount_ VeryLongEventName
|
||||
|
||||
200 (0.2%) 100 (0.1%) 0 one
|
||||
. . . two
|
||||
. . . three
|
||||
200 (0.2%) 100 (0.1%) 0 one
|
||||
. . . two
|
||||
. . . three
|
||||
-- line 3 ----------------------------------------
|
||||
|
||||
300 (0.3%) 100 (0.1%) 0 <bogus line 20>
|
||||
300 (0.3%) 100 (0.1%) 0 <bogus line 21>
|
||||
200 (0.2%) 0 -1,000 (n/a) <bogus line 22>
|
||||
300 (0.3%) 100 (0.1%) 0 <bogus line 20>
|
||||
300 (0.3%) 100 (0.1%) 0 <bogus line 21>
|
||||
200 (0.2%) 0 -1,000 (n/a) <bogus line 22>
|
||||
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
@@ WARNING @@ WARNING @@ WARNING @@ WARNING @@ WARNING @@ WARNING @@ WARNING @@
|
||||
@@ -133,18 +174,18 @@ A SomeCount VeryLongEventName
|
||||
--------------------------------------------------------------------------------
|
||||
-- Annotated source file: ann2-aux/ann2-via-I.rs
|
||||
--------------------------------------------------------------------------------
|
||||
A SomeCount VeryLongEventName
|
||||
A___________ SomeCount_ VeryLongEventName
|
||||
|
||||
1,000 (1.0%) 500 (0.5%) 0 one
|
||||
1,000 (1.0%) 500 (0.5%) 0 one
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Annotation summary
|
||||
--------------------------------------------------------------------------------
|
||||
A SomeCount VeryLongEventName
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user