xref: /llvm-project/llvm/utils/llvm-locstats/llvm-locstats.py (revision be031b17191efb32636a05d2e39433446c328d92)
1*be031b17SAiden Grossman#!/usr/bin/env python3
22ef18fb4SDjordje Todorovic#
32ef18fb4SDjordje Todorovic# This is a tool that works like debug location coverage calculator.
42ef18fb4SDjordje Todorovic# It parses the llvm-dwarfdump --statistics output by reporting it
52ef18fb4SDjordje Todorovic# in a more human readable way.
62ef18fb4SDjordje Todorovic#
72ef18fb4SDjordje Todorovic
82ef18fb4SDjordje Todorovicfrom __future__ import print_function
92ef18fb4SDjordje Todorovicimport argparse
102ef18fb4SDjordje Todorovicimport os
112ef18fb4SDjordje Todorovicimport sys
122ef18fb4SDjordje Todorovicfrom json import loads
132ef18fb4SDjordje Todorovicfrom math import ceil
14ada96466SDjordje Todorovicfrom collections import OrderedDict
152ef18fb4SDjordje Todorovicfrom subprocess import Popen, PIPE
162ef18fb4SDjordje Todorovic
1730a3652bSdjtodoro# This special value has been used to mark statistics that overflowed.
1830a3652bSdjtodoroTAINT_VALUE = "tainted"
1930a3652bSdjtodoro
203b8ef787SDjordje Todorovic# Initialize the plot.
213b8ef787SDjordje Todorovicdef init_plot(plt):
22b71edfaaSTobias Hieta    plt.title("Debug Location Statistics", fontweight="bold")
23b71edfaaSTobias Hieta    plt.xlabel("location buckets")
24b71edfaaSTobias Hieta    plt.ylabel("number of variables in the location buckets")
25b71edfaaSTobias Hieta    plt.xticks(rotation=45, fontsize="x-small")
263b8ef787SDjordje Todorovic    plt.yticks()
273b8ef787SDjordje Todorovic
28b71edfaaSTobias Hieta
293b8ef787SDjordje Todorovic# Finalize the plot.
303b8ef787SDjordje Todorovicdef finish_plot(plt):
313b8ef787SDjordje Todorovic    plt.legend()
32b71edfaaSTobias Hieta    plt.grid(color="grey", which="major", axis="y", linestyle="-", linewidth=0.3)
33b71edfaaSTobias Hieta    plt.savefig("locstats.png")
343b8ef787SDjordje Todorovic    print('The plot was saved within "locstats.png".')
353b8ef787SDjordje Todorovic
36b71edfaaSTobias Hieta
37a3ebc406SDjordje Todorovic# Holds the debug location statistics.
38a3ebc406SDjordje Todorovicclass LocationStats:
39b71edfaaSTobias Hieta    def __init__(
40b71edfaaSTobias Hieta        self,
41b71edfaaSTobias Hieta        file_name,
42b71edfaaSTobias Hieta        variables_total,
43b71edfaaSTobias Hieta        variables_total_locstats,
44b71edfaaSTobias Hieta        variables_with_loc,
45b71edfaaSTobias Hieta        variables_scope_bytes_covered,
46b71edfaaSTobias Hieta        variables_scope_bytes,
47b71edfaaSTobias Hieta        variables_coverage_map,
48b71edfaaSTobias Hieta    ):
49a3ebc406SDjordje Todorovic        self.file_name = file_name
50a3ebc406SDjordje Todorovic        self.variables_total = variables_total
51a3ebc406SDjordje Todorovic        self.variables_total_locstats = variables_total_locstats
52a3ebc406SDjordje Todorovic        self.variables_with_loc = variables_with_loc
53a3ebc406SDjordje Todorovic        self.scope_bytes_covered = variables_scope_bytes_covered
54a3ebc406SDjordje Todorovic        self.scope_bytes = variables_scope_bytes
55a3ebc406SDjordje Todorovic        self.variables_coverage_map = variables_coverage_map
562ef18fb4SDjordje Todorovic
57ada96466SDjordje Todorovic    # Get the PC ranges coverage.
58ada96466SDjordje Todorovic    def get_pc_coverage(self):
59b71edfaaSTobias Hieta        if self.scope_bytes_covered == TAINT_VALUE or self.scope_bytes == TAINT_VALUE:
6030a3652bSdjtodoro            return TAINT_VALUE
61b71edfaaSTobias Hieta        pc_ranges_covered = int(
62b71edfaaSTobias Hieta            ceil(self.scope_bytes_covered * 100.0) / self.scope_bytes
63b71edfaaSTobias Hieta        )
64ada96466SDjordje Todorovic        return pc_ranges_covered
65ada96466SDjordje Todorovic
66a3ebc406SDjordje Todorovic    # Pretty print the debug location buckets.
67a3ebc406SDjordje Todorovic    def pretty_print(self):
68a3ebc406SDjordje Todorovic        if self.scope_bytes == 0:
69b71edfaaSTobias Hieta            print("No scope bytes found.")
70a3ebc406SDjordje Todorovic            return -1
71baea9136SDjordje Todorovic
72ada96466SDjordje Todorovic        pc_ranges_covered = self.get_pc_coverage()
732ef18fb4SDjordje Todorovic        variables_coverage_per_map = {}
742ef18fb4SDjordje Todorovic        for cov_bucket in coverage_buckets():
7530a3652bSdjtodoro            variables_coverage_per_map[cov_bucket] = None
76b71edfaaSTobias Hieta            if (
77b71edfaaSTobias Hieta                self.variables_coverage_map[cov_bucket] == TAINT_VALUE
78b71edfaaSTobias Hieta                or self.variables_total_locstats == TAINT_VALUE
79b71edfaaSTobias Hieta            ):
8030a3652bSdjtodoro                variables_coverage_per_map[cov_bucket] = TAINT_VALUE
8130a3652bSdjtodoro            else:
82b71edfaaSTobias Hieta                variables_coverage_per_map[cov_bucket] = int(
83b71edfaaSTobias Hieta                    ceil(self.variables_coverage_map[cov_bucket] * 100.0)
84b71edfaaSTobias Hieta                    / self.variables_total_locstats
85b71edfaaSTobias Hieta                )
862ef18fb4SDjordje Todorovic
87b71edfaaSTobias Hieta        print(" =================================================")
88b71edfaaSTobias Hieta        print("            Debug Location Statistics       ")
89b71edfaaSTobias Hieta        print(" =================================================")
90b71edfaaSTobias Hieta        print("     cov%           samples         percentage(~)  ")
91b71edfaaSTobias Hieta        print(" -------------------------------------------------")
922ef18fb4SDjordje Todorovic        for cov_bucket in coverage_buckets():
93b71edfaaSTobias Hieta            if (
94b71edfaaSTobias Hieta                self.variables_coverage_map[cov_bucket]
95b71edfaaSTobias Hieta                or self.variables_total_locstats == TAINT_VALUE
96b71edfaaSTobias Hieta            ):
97b71edfaaSTobias Hieta                print(
98b71edfaaSTobias Hieta                    "   {0:10}     {1:8}              {2:3}%".format(
99b71edfaaSTobias Hieta                        cov_bucket,
100b71edfaaSTobias Hieta                        self.variables_coverage_map[cov_bucket],
101b71edfaaSTobias Hieta                        variables_coverage_per_map[cov_bucket],
102b71edfaaSTobias Hieta                    )
103b71edfaaSTobias Hieta                )
10430a3652bSdjtodoro            else:
105b71edfaaSTobias Hieta                print(
106b71edfaaSTobias Hieta                    "   {0:10}     {1:8d}              {2:3d}%".format(
107b71edfaaSTobias Hieta                        cov_bucket,
108b71edfaaSTobias Hieta                        self.variables_coverage_map[cov_bucket],
109b71edfaaSTobias Hieta                        variables_coverage_per_map[cov_bucket],
110b71edfaaSTobias Hieta                    )
111b71edfaaSTobias Hieta                )
112b71edfaaSTobias Hieta        print(" =================================================")
113b71edfaaSTobias Hieta        print(
114b71edfaaSTobias Hieta            " -the number of debug variables processed: "
115b71edfaaSTobias Hieta            + str(self.variables_total_locstats)
116b71edfaaSTobias Hieta        )
117b71edfaaSTobias Hieta        print(" -PC ranges covered: " + str(pc_ranges_covered) + "%")
1182ef18fb4SDjordje Todorovic
1192ef18fb4SDjordje Todorovic        # Only if we are processing all the variables output the total
1202ef18fb4SDjordje Todorovic        # availability.
121a3ebc406SDjordje Todorovic        if self.variables_total and self.variables_with_loc:
12230a3652bSdjtodoro            total_availability = None
123b71edfaaSTobias Hieta            if (
124b71edfaaSTobias Hieta                self.variables_total == TAINT_VALUE
125b71edfaaSTobias Hieta                or self.variables_with_loc == TAINT_VALUE
126b71edfaaSTobias Hieta            ):
12730a3652bSdjtodoro                total_availability = TAINT_VALUE
12830a3652bSdjtodoro            else:
129b71edfaaSTobias Hieta                total_availability = int(
130b71edfaaSTobias Hieta                    ceil(self.variables_with_loc * 100.0) / self.variables_total
131b71edfaaSTobias Hieta                )
132b71edfaaSTobias Hieta            print(" -------------------------------------------------")
133b71edfaaSTobias Hieta            print(" -total availability: " + str(total_availability) + "%")
134b71edfaaSTobias Hieta        print(" =================================================")
1352ef18fb4SDjordje Todorovic
136a3ebc406SDjordje Todorovic        return 0
1372ef18fb4SDjordje Todorovic
138ada96466SDjordje Todorovic    # Draw a plot representing the location buckets.
139ada96466SDjordje Todorovic    def draw_plot(self):
140ada96466SDjordje Todorovic        from matplotlib import pyplot as plt
141ada96466SDjordje Todorovic
142ada96466SDjordje Todorovic        buckets = range(len(self.variables_coverage_map))
143ada96466SDjordje Todorovic        plt.figure(figsize=(12, 8))
1443b8ef787SDjordje Todorovic        init_plot(plt)
145b71edfaaSTobias Hieta        plt.bar(
146b71edfaaSTobias Hieta            buckets,
147b71edfaaSTobias Hieta            self.variables_coverage_map.values(),
148b71edfaaSTobias Hieta            align="center",
149ada96466SDjordje Todorovic            tick_label=self.variables_coverage_map.keys(),
150b71edfaaSTobias Hieta            label="variables of {}".format(self.file_name),
151b71edfaaSTobias Hieta        )
152ada96466SDjordje Todorovic
153ada96466SDjordje Todorovic        # Place the text box with the coverage info.
154ada96466SDjordje Todorovic        pc_ranges_covered = self.get_pc_coverage()
155b71edfaaSTobias Hieta        props = dict(boxstyle="round", facecolor="wheat", alpha=0.5)
156b71edfaaSTobias Hieta        plt.text(
157b71edfaaSTobias Hieta            0.02,
158b71edfaaSTobias Hieta            0.90,
159b71edfaaSTobias Hieta            "PC ranges covered: {}%".format(pc_ranges_covered),
160b71edfaaSTobias Hieta            transform=plt.gca().transAxes,
161b71edfaaSTobias Hieta            fontsize=12,
162b71edfaaSTobias Hieta            verticalalignment="top",
163b71edfaaSTobias Hieta            bbox=props,
164b71edfaaSTobias Hieta        )
165ada96466SDjordje Todorovic
1663b8ef787SDjordje Todorovic        finish_plot(plt)
1673b8ef787SDjordje Todorovic
1683b8ef787SDjordje Todorovic    # Compare the two LocationStats objects and draw a plot showing
1693b8ef787SDjordje Todorovic    # the difference.
1703b8ef787SDjordje Todorovic    def draw_location_diff(self, locstats_to_compare):
1713b8ef787SDjordje Todorovic        from matplotlib import pyplot as plt
1723b8ef787SDjordje Todorovic
1733b8ef787SDjordje Todorovic        pc_ranges_covered = self.get_pc_coverage()
1743b8ef787SDjordje Todorovic        pc_ranges_covered_to_compare = locstats_to_compare.get_pc_coverage()
1753b8ef787SDjordje Todorovic
1763b8ef787SDjordje Todorovic        buckets = range(len(self.variables_coverage_map))
1773b8ef787SDjordje Todorovic        buckets_to_compare = range(len(locstats_to_compare.variables_coverage_map))
1783b8ef787SDjordje Todorovic
1793b8ef787SDjordje Todorovic        fig = plt.figure(figsize=(12, 8))
1803b8ef787SDjordje Todorovic        ax = fig.add_subplot(111)
1813b8ef787SDjordje Todorovic        init_plot(plt)
1823b8ef787SDjordje Todorovic
1833a7865dfSVedant Kumar        comparison_keys = list(coverage_buckets())
184b71edfaaSTobias Hieta        ax.bar(
185b71edfaaSTobias Hieta            buckets,
186b71edfaaSTobias Hieta            self.variables_coverage_map.values(),
187b71edfaaSTobias Hieta            align="edge",
1883a7865dfSVedant Kumar            width=0.4,
189b71edfaaSTobias Hieta            label="variables of {}".format(self.file_name),
190b71edfaaSTobias Hieta        )
191b71edfaaSTobias Hieta        ax.bar(
192b71edfaaSTobias Hieta            buckets_to_compare,
1933b8ef787SDjordje Todorovic            locstats_to_compare.variables_coverage_map.values(),
194b71edfaaSTobias Hieta            color="r",
195b71edfaaSTobias Hieta            align="edge",
196b71edfaaSTobias Hieta            width=-0.4,
197b71edfaaSTobias Hieta            label="variables of {}".format(locstats_to_compare.file_name),
198b71edfaaSTobias Hieta        )
1993a7865dfSVedant Kumar        ax.set_xticks(range(len(comparison_keys)))
2003a7865dfSVedant Kumar        ax.set_xticklabels(comparison_keys)
2013b8ef787SDjordje Todorovic
202b71edfaaSTobias Hieta        props = dict(boxstyle="round", facecolor="wheat", alpha=0.5)
203b71edfaaSTobias Hieta        plt.text(
204b71edfaaSTobias Hieta            0.02,
205b71edfaaSTobias Hieta            0.88,
206b71edfaaSTobias Hieta            "{} PC ranges covered: {}%".format(self.file_name, pc_ranges_covered),
207b71edfaaSTobias Hieta            transform=plt.gca().transAxes,
208b71edfaaSTobias Hieta            fontsize=12,
209b71edfaaSTobias Hieta            verticalalignment="top",
210b71edfaaSTobias Hieta            bbox=props,
211b71edfaaSTobias Hieta        )
212b71edfaaSTobias Hieta        plt.text(
213b71edfaaSTobias Hieta            0.02,
214b71edfaaSTobias Hieta            0.83,
215b71edfaaSTobias Hieta            "{} PC ranges covered: {}%".format(
216b71edfaaSTobias Hieta                locstats_to_compare.file_name, pc_ranges_covered_to_compare
217b71edfaaSTobias Hieta            ),
218b71edfaaSTobias Hieta            transform=plt.gca().transAxes,
219b71edfaaSTobias Hieta            fontsize=12,
220b71edfaaSTobias Hieta            verticalalignment="top",
221b71edfaaSTobias Hieta            bbox=props,
222b71edfaaSTobias Hieta        )
2233b8ef787SDjordje Todorovic
2243b8ef787SDjordje Todorovic        finish_plot(plt)
225ada96466SDjordje Todorovic
226b71edfaaSTobias Hieta
227a3ebc406SDjordje Todorovic# Define the location buckets.
228a3ebc406SDjordje Todorovicdef coverage_buckets():
229b71edfaaSTobias Hieta    yield "0%"
230b71edfaaSTobias Hieta    yield "(0%,10%)"
231a3ebc406SDjordje Todorovic    for start in range(10, 91, 10):
232b71edfaaSTobias Hieta        yield "[{0}%,{1}%)".format(start, start + 10)
233b71edfaaSTobias Hieta    yield "100%"
234b71edfaaSTobias Hieta
2352ef18fb4SDjordje Todorovic
236a3ebc406SDjordje Todorovic# Parse the JSON representing the debug statistics, and create a
237a3ebc406SDjordje Todorovic# LocationStats object.
238a3ebc406SDjordje Todorovicdef parse_locstats(opts, binary):
2392ef18fb4SDjordje Todorovic    # These will be different due to different options enabled.
2402ef18fb4SDjordje Todorovic    variables_total = None
2412ef18fb4SDjordje Todorovic    variables_total_locstats = None
2422ef18fb4SDjordje Todorovic    variables_with_loc = None
2432ef18fb4SDjordje Todorovic    variables_scope_bytes_covered = None
24468f464acSKristina Bessonova    variables_scope_bytes = None
2452ef18fb4SDjordje Todorovic    variables_scope_bytes_entry_values = None
246ada96466SDjordje Todorovic    variables_coverage_map = OrderedDict()
2472ef18fb4SDjordje Todorovic
2482ef18fb4SDjordje Todorovic    # Get the directory of the LLVM tools.
249b71edfaaSTobias Hieta    llvm_dwarfdump_cmd = os.path.join(os.path.dirname(__file__), "llvm-dwarfdump")
2502ef18fb4SDjordje Todorovic    # The statistics llvm-dwarfdump option.
2512ef18fb4SDjordje Todorovic    llvm_dwarfdump_stats_opt = "--statistics"
2522ef18fb4SDjordje Todorovic
253a3ebc406SDjordje Todorovic    # Generate the stats with the llvm-dwarfdump.
254b71edfaaSTobias Hieta    subproc = Popen(
255b71edfaaSTobias Hieta        [llvm_dwarfdump_cmd, llvm_dwarfdump_stats_opt, binary],
256b71edfaaSTobias Hieta        stdin=PIPE,
257b71edfaaSTobias Hieta        stdout=PIPE,
258b71edfaaSTobias Hieta        stderr=PIPE,
259b71edfaaSTobias Hieta        universal_newlines=True,
260b71edfaaSTobias Hieta    )
2612ef18fb4SDjordje Todorovic    cmd_stdout, cmd_stderr = subproc.communicate()
2622ef18fb4SDjordje Todorovic
26330a3652bSdjtodoro    # TODO: Handle errors that are coming from llvm-dwarfdump.
26430a3652bSdjtodoro
2652ef18fb4SDjordje Todorovic    # Get the JSON and parse it.
2662ef18fb4SDjordje Todorovic    json_parsed = None
2672ef18fb4SDjordje Todorovic
2682ef18fb4SDjordje Todorovic    try:
2692ef18fb4SDjordje Todorovic        json_parsed = loads(cmd_stdout)
2702ef18fb4SDjordje Todorovic    except:
271b71edfaaSTobias Hieta        print("error: No valid llvm-dwarfdump statistics found.")
2722ef18fb4SDjordje Todorovic        sys.exit(1)
2732ef18fb4SDjordje Todorovic
2740a4defe8SDjordje Todorovic    # TODO: Parse the statistics Version from JSON.
2750a4defe8SDjordje Todorovic
27630a3652bSdjtodoro    def init_field(name):
277b71edfaaSTobias Hieta        if json_parsed[name] == "overflowed":
27830a3652bSdjtodoro            print('warning: "' + name + '" field overflowed.')
27930a3652bSdjtodoro            return TAINT_VALUE
28030a3652bSdjtodoro        return json_parsed[name]
28130a3652bSdjtodoro
282a3ebc406SDjordje Todorovic    if opts.only_variables:
2832ef18fb4SDjordje Todorovic        # Read the JSON only for local variables.
284b71edfaaSTobias Hieta        variables_total_locstats = init_field(
285b71edfaaSTobias Hieta            "#local vars processed by location statistics"
286b71edfaaSTobias Hieta        )
287b71edfaaSTobias Hieta        variables_scope_bytes_covered = init_field(
288b71edfaaSTobias Hieta            "sum_all_local_vars(#bytes in parent scope covered" " by DW_AT_location)"
289b71edfaaSTobias Hieta        )
290b71edfaaSTobias Hieta        variables_scope_bytes = init_field("sum_all_local_vars(#bytes in parent scope)")
291a3ebc406SDjordje Todorovic        if not opts.ignore_debug_entry_values:
2922ef18fb4SDjordje Todorovic            for cov_bucket in coverage_buckets():
293b71edfaaSTobias Hieta                cov_category = (
294b71edfaaSTobias Hieta                    "#local vars with {} of parent scope covered "
2950a4defe8SDjordje Todorovic                    "by DW_AT_location".format(cov_bucket)
296b71edfaaSTobias Hieta                )
29730a3652bSdjtodoro                variables_coverage_map[cov_bucket] = init_field(cov_category)
2982ef18fb4SDjordje Todorovic        else:
299b71edfaaSTobias Hieta            variables_scope_bytes_entry_values = init_field(
300b71edfaaSTobias Hieta                "sum_all_local_vars(#bytes in parent scope "
301b71edfaaSTobias Hieta                "covered by DW_OP_entry_value)"
302b71edfaaSTobias Hieta            )
303b71edfaaSTobias Hieta            if (
304b71edfaaSTobias Hieta                variables_scope_bytes_covered != TAINT_VALUE
305b71edfaaSTobias Hieta                and variables_scope_bytes_entry_values != TAINT_VALUE
306b71edfaaSTobias Hieta            ):
307b71edfaaSTobias Hieta                variables_scope_bytes_covered = (
308b71edfaaSTobias Hieta                    variables_scope_bytes_covered - variables_scope_bytes_entry_values
309b71edfaaSTobias Hieta                )
3102ef18fb4SDjordje Todorovic            for cov_bucket in coverage_buckets():
311b71edfaaSTobias Hieta                cov_category = (
312b71edfaaSTobias Hieta                    "#local vars - entry values with {} of parent scope "
3130a4defe8SDjordje Todorovic                    "covered by DW_AT_location".format(cov_bucket)
314b71edfaaSTobias Hieta                )
31530a3652bSdjtodoro                variables_coverage_map[cov_bucket] = init_field(cov_category)
316a3ebc406SDjordje Todorovic    elif opts.only_formal_parameters:
3172ef18fb4SDjordje Todorovic        # Read the JSON only for formal parameters.
318b71edfaaSTobias Hieta        variables_total_locstats = init_field(
319b71edfaaSTobias Hieta            "#params processed by location statistics"
320b71edfaaSTobias Hieta        )
321b71edfaaSTobias Hieta        variables_scope_bytes_covered = init_field(
322b71edfaaSTobias Hieta            "sum_all_params(#bytes in parent scope covered " "by DW_AT_location)"
323b71edfaaSTobias Hieta        )
324b71edfaaSTobias Hieta        variables_scope_bytes = init_field("sum_all_params(#bytes in parent scope)")
325a3ebc406SDjordje Todorovic        if not opts.ignore_debug_entry_values:
3262ef18fb4SDjordje Todorovic            for cov_bucket in coverage_buckets():
327b71edfaaSTobias Hieta                cov_category = (
328b71edfaaSTobias Hieta                    "#params with {} of parent scope covered "
3290a4defe8SDjordje Todorovic                    "by DW_AT_location".format(cov_bucket)
330b71edfaaSTobias Hieta                )
33130a3652bSdjtodoro                variables_coverage_map[cov_bucket] = init_field(cov_category)
3322ef18fb4SDjordje Todorovic        else:
333b71edfaaSTobias Hieta            variables_scope_bytes_entry_values = init_field(
334b71edfaaSTobias Hieta                "sum_all_params(#bytes in parent scope covered " "by DW_OP_entry_value)"
335b71edfaaSTobias Hieta            )
336b71edfaaSTobias Hieta            if (
337b71edfaaSTobias Hieta                variables_scope_bytes_covered != TAINT_VALUE
338b71edfaaSTobias Hieta                and variables_scope_bytes_entry_values != TAINT_VALUE
339b71edfaaSTobias Hieta            ):
340b71edfaaSTobias Hieta                variables_scope_bytes_covered = (
341b71edfaaSTobias Hieta                    variables_scope_bytes_covered - variables_scope_bytes_entry_values
342b71edfaaSTobias Hieta                )
3432ef18fb4SDjordje Todorovic            for cov_bucket in coverage_buckets():
344b71edfaaSTobias Hieta                cov_category = (
345b71edfaaSTobias Hieta                    "#params - entry values with {} of parent scope covered"
3460a4defe8SDjordje Todorovic                    " by DW_AT_location".format(cov_bucket)
347b71edfaaSTobias Hieta                )
34830a3652bSdjtodoro                variables_coverage_map[cov_bucket] = init_field(cov_category)
3492ef18fb4SDjordje Todorovic    else:
3502ef18fb4SDjordje Todorovic        # Read the JSON for both local variables and formal parameters.
351b71edfaaSTobias Hieta        variables_total = init_field("#source variables")
352b71edfaaSTobias Hieta        variables_with_loc = init_field("#source variables with location")
353b71edfaaSTobias Hieta        variables_total_locstats = init_field(
354b71edfaaSTobias Hieta            "#variables processed by location statistics"
355b71edfaaSTobias Hieta        )
356b71edfaaSTobias Hieta        variables_scope_bytes_covered = init_field(
357b71edfaaSTobias Hieta            "sum_all_variables(#bytes in parent scope covered " "by DW_AT_location)"
358b71edfaaSTobias Hieta        )
359b71edfaaSTobias Hieta        variables_scope_bytes = init_field("sum_all_variables(#bytes in parent scope)")
36030a3652bSdjtodoro
361a3ebc406SDjordje Todorovic        if not opts.ignore_debug_entry_values:
3622ef18fb4SDjordje Todorovic            for cov_bucket in coverage_buckets():
363b71edfaaSTobias Hieta                cov_category = (
364b71edfaaSTobias Hieta                    "#variables with {} of parent scope covered "
3650a4defe8SDjordje Todorovic                    "by DW_AT_location".format(cov_bucket)
366b71edfaaSTobias Hieta                )
36730a3652bSdjtodoro                variables_coverage_map[cov_bucket] = init_field(cov_category)
3682ef18fb4SDjordje Todorovic        else:
369b71edfaaSTobias Hieta            variables_scope_bytes_entry_values = init_field(
370b71edfaaSTobias Hieta                "sum_all_variables(#bytes in parent scope covered "
371b71edfaaSTobias Hieta                "by DW_OP_entry_value)"
372b71edfaaSTobias Hieta            )
373b71edfaaSTobias Hieta            if (
374b71edfaaSTobias Hieta                variables_scope_bytes_covered != TAINT_VALUE
375b71edfaaSTobias Hieta                and variables_scope_bytes_entry_values != TAINT_VALUE
376b71edfaaSTobias Hieta            ):
377b71edfaaSTobias Hieta                variables_scope_bytes_covered = (
378b71edfaaSTobias Hieta                    variables_scope_bytes_covered - variables_scope_bytes_entry_values
379b71edfaaSTobias Hieta                )
3802ef18fb4SDjordje Todorovic            for cov_bucket in coverage_buckets():
381b71edfaaSTobias Hieta                cov_category = (
382b71edfaaSTobias Hieta                    "#variables - entry values with {} of parent scope covered "
3830a4defe8SDjordje Todorovic                    "by DW_AT_location".format(cov_bucket)
384b71edfaaSTobias Hieta                )
38530a3652bSdjtodoro                variables_coverage_map[cov_bucket] = init_field(cov_category)
3862ef18fb4SDjordje Todorovic
387b71edfaaSTobias Hieta    return LocationStats(
388b71edfaaSTobias Hieta        binary,
389b71edfaaSTobias Hieta        variables_total,
390b71edfaaSTobias Hieta        variables_total_locstats,
391b71edfaaSTobias Hieta        variables_with_loc,
392b71edfaaSTobias Hieta        variables_scope_bytes_covered,
393b71edfaaSTobias Hieta        variables_scope_bytes,
394b71edfaaSTobias Hieta        variables_coverage_map,
395b71edfaaSTobias Hieta    )
396b71edfaaSTobias Hieta
397a3ebc406SDjordje Todorovic
398a3ebc406SDjordje Todorovic# Parse the program arguments.
399a3ebc406SDjordje Todorovicdef parse_program_args(parser):
400b71edfaaSTobias Hieta    parser.add_argument(
401b71edfaaSTobias Hieta        "--only-variables",
402b71edfaaSTobias Hieta        action="store_true",
403a3ebc406SDjordje Todorovic        default=False,
404b71edfaaSTobias Hieta        help="calculate the location statistics only for local variables",
405b71edfaaSTobias Hieta    )
406b71edfaaSTobias Hieta    parser.add_argument(
407b71edfaaSTobias Hieta        "--only-formal-parameters",
408b71edfaaSTobias Hieta        action="store_true",
409a3ebc406SDjordje Todorovic        default=False,
410b71edfaaSTobias Hieta        help="calculate the location statistics only for formal parameters",
411b71edfaaSTobias Hieta    )
412b71edfaaSTobias Hieta    parser.add_argument(
413b71edfaaSTobias Hieta        "--ignore-debug-entry-values",
414b71edfaaSTobias Hieta        action="store_true",
415b71edfaaSTobias Hieta        default=False,
416b71edfaaSTobias Hieta        help="ignore the location statistics on locations with " "entry values",
417b71edfaaSTobias Hieta    )
418b71edfaaSTobias Hieta    parser.add_argument(
419b71edfaaSTobias Hieta        "--draw-plot",
420b71edfaaSTobias Hieta        action="store_true",
421b71edfaaSTobias Hieta        default=False,
422b71edfaaSTobias Hieta        help="show histogram of location buckets generated (requires " "matplotlib)",
423b71edfaaSTobias Hieta    )
424b71edfaaSTobias Hieta    parser.add_argument(
425b71edfaaSTobias Hieta        "--compare",
426b71edfaaSTobias Hieta        action="store_true",
427b71edfaaSTobias Hieta        default=False,
428b71edfaaSTobias Hieta        help="compare the debug location coverage on two files provided, "
429b71edfaaSTobias Hieta        "and draw a plot showing the difference  (requires "
430b71edfaaSTobias Hieta        "matplotlib)",
431b71edfaaSTobias Hieta    )
432b71edfaaSTobias Hieta    parser.add_argument("file_names", nargs="+", type=str, help="file to process")
433a3ebc406SDjordje Todorovic
434a3ebc406SDjordje Todorovic    return parser.parse_args()
435a3ebc406SDjordje Todorovic
436b71edfaaSTobias Hieta
437a3ebc406SDjordje Todorovic# Verify that the program inputs meet the requirements.
438a3ebc406SDjordje Todorovicdef verify_program_inputs(opts):
439a3ebc406SDjordje Todorovic    if len(sys.argv) < 2:
440b71edfaaSTobias Hieta        print("error: Too few arguments.")
441a3ebc406SDjordje Todorovic        return False
442a3ebc406SDjordje Todorovic
443a3ebc406SDjordje Todorovic    if opts.only_variables and opts.only_formal_parameters:
444b71edfaaSTobias Hieta        print("error: Please use just one --only* option.")
445a3ebc406SDjordje Todorovic        return False
446a3ebc406SDjordje Todorovic
4473b8ef787SDjordje Todorovic    if not opts.compare and len(opts.file_names) != 1:
448b71edfaaSTobias Hieta        print("error: Please specify only one file to process.")
4493b8ef787SDjordje Todorovic        return False
4503b8ef787SDjordje Todorovic
4513b8ef787SDjordje Todorovic    if opts.compare and len(opts.file_names) != 2:
452b71edfaaSTobias Hieta        print("error: Please specify two files to process.")
4533b8ef787SDjordje Todorovic        return False
4543b8ef787SDjordje Todorovic
4553b8ef787SDjordje Todorovic    if opts.draw_plot or opts.compare:
4563b8ef787SDjordje Todorovic        try:
4573b8ef787SDjordje Todorovic            import matplotlib
4583b8ef787SDjordje Todorovic        except ImportError:
459b71edfaaSTobias Hieta            print("error: matplotlib not found.")
4603b8ef787SDjordje Todorovic            return False
4613b8ef787SDjordje Todorovic
462a3ebc406SDjordje Todorovic    return True
463a3ebc406SDjordje Todorovic
464b71edfaaSTobias Hieta
465a3ebc406SDjordje Todorovicdef Main():
466a3ebc406SDjordje Todorovic    parser = argparse.ArgumentParser()
467a3ebc406SDjordje Todorovic    opts = parse_program_args(parser)
468a3ebc406SDjordje Todorovic
469a3ebc406SDjordje Todorovic    if not verify_program_inputs(opts):
470a3ebc406SDjordje Todorovic        parser.print_help()
471a3ebc406SDjordje Todorovic        sys.exit(1)
472a3ebc406SDjordje Todorovic
4733b8ef787SDjordje Todorovic    binary_file = opts.file_names[0]
4743b8ef787SDjordje Todorovic    locstats = parse_locstats(opts, binary_file)
475a3ebc406SDjordje Todorovic
4763b8ef787SDjordje Todorovic    if not opts.compare:
477ada96466SDjordje Todorovic        if opts.draw_plot:
478ada96466SDjordje Todorovic            # Draw a histogram representing the location buckets.
479ada96466SDjordje Todorovic            locstats.draw_plot()
480ada96466SDjordje Todorovic        else:
481ada96466SDjordje Todorovic            # Pretty print collected info on the standard output.
482a3ebc406SDjordje Todorovic            if locstats.pretty_print() == -1:
483a3ebc406SDjordje Todorovic                sys.exit(0)
4843b8ef787SDjordje Todorovic    else:
4853b8ef787SDjordje Todorovic        binary_file_to_compare = opts.file_names[1]
4863b8ef787SDjordje Todorovic        locstats_to_compare = parse_locstats(opts, binary_file_to_compare)
4873b8ef787SDjordje Todorovic        # Draw a plot showing the difference in debug location coverage between
4883b8ef787SDjordje Todorovic        # two files.
4893b8ef787SDjordje Todorovic        locstats.draw_location_diff(locstats_to_compare)
4902ef18fb4SDjordje Todorovic
491b71edfaaSTobias Hieta
492b71edfaaSTobias Hietaif __name__ == "__main__":
4932ef18fb4SDjordje Todorovic    Main()
4942ef18fb4SDjordje Todorovic    sys.exit(0)
495