116236077SArtem Dergachev#!/usr/bin/env python 244fb55bfSArtem Dergachev# 344fb55bfSArtem Dergachev# ===- exploded-graph-rewriter.py - ExplodedGraph dump tool -----*- python -*--# 444fb55bfSArtem Dergachev# 544fb55bfSArtem Dergachev# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 644fb55bfSArtem Dergachev# See https://llvm.org/LICENSE.txt for license information. 744fb55bfSArtem Dergachev# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 844fb55bfSArtem Dergachev# 944fb55bfSArtem Dergachev# ===-----------------------------------------------------------------------===# 1044fb55bfSArtem Dergachev 1116236077SArtem Dergachev 1216236077SArtem Dergachevfrom __future__ import print_function 1316236077SArtem Dergachev 1416236077SArtem Dergachevimport argparse 1516236077SArtem Dergachevimport collections 16deb7accbSArtem Dergachevimport difflib 1716236077SArtem Dergachevimport json 1816236077SArtem Dergachevimport logging 19fc6059e8SArtem Dergachevimport os 2016236077SArtem Dergachevimport re 2116236077SArtem Dergachev 2216236077SArtem Dergachev 235fcf92e1SArtem Dergachev# ===-----------------------------------------------------------------------===# 245fcf92e1SArtem Dergachev# These data structures represent a deserialized ExplodedGraph. 255fcf92e1SArtem Dergachev# ===-----------------------------------------------------------------------===# 265fcf92e1SArtem Dergachev 275fcf92e1SArtem Dergachev 285740e77fSArtem Dergachev# A helper function for finding the difference between two dictionaries. 295740e77fSArtem Dergachevdef diff_dicts(curr, prev): 305740e77fSArtem Dergachev removed = [k for k in prev if k not in curr or curr[k] != prev[k]] 315740e77fSArtem Dergachev added = [k for k in curr if k not in prev or curr[k] != prev[k]] 325740e77fSArtem Dergachev return (removed, added) 335740e77fSArtem Dergachev 345740e77fSArtem Dergachev 35beb85ad6SArtem Dergachev# Represents any program state trait that is a dictionary of key-value pairs. 36c98872e3SValeriy Savchenkoclass GenericMap: 3702f91ddfSArtem Dergachev def __init__(self, items): 3802f91ddfSArtem Dergachev self.generic_map = collections.OrderedDict(items) 39beb85ad6SArtem Dergachev 40beb85ad6SArtem Dergachev def diff(self, prev): 41beb85ad6SArtem Dergachev return diff_dicts(self.generic_map, prev.generic_map) 42beb85ad6SArtem Dergachev 43beb85ad6SArtem Dergachev def is_different(self, prev): 44beb85ad6SArtem Dergachev removed, added = self.diff(prev) 45beb85ad6SArtem Dergachev return len(removed) != 0 or len(added) != 0 46beb85ad6SArtem Dergachev 47beb85ad6SArtem Dergachev 4816236077SArtem Dergachev# A deserialized source location. 49c98872e3SValeriy Savchenkoclass SourceLocation: 5016236077SArtem Dergachev def __init__(self, json_loc): 51dd3c26a0STobias Hieta logging.debug("json: %s" % json_loc) 52dd3c26a0STobias Hieta self.line = json_loc["line"] 53dd3c26a0STobias Hieta self.col = json_loc["column"] 54dd3c26a0STobias Hieta self.filename = ( 55dd3c26a0STobias Hieta os.path.basename(json_loc["file"]) if "file" in json_loc else "(main file)" 56dd3c26a0STobias Hieta ) 57dd3c26a0STobias Hieta self.spelling = ( 58dd3c26a0STobias Hieta SourceLocation(json_loc["spelling"]) if "spelling" in json_loc else None 59dd3c26a0STobias Hieta ) 60ed035ff8SArtem Dergachev 61ed035ff8SArtem Dergachev def is_macro(self): 62ed035ff8SArtem Dergachev return self.spelling is not None 6316236077SArtem Dergachev 6416236077SArtem Dergachev 6516236077SArtem Dergachev# A deserialized program point. 66c98872e3SValeriy Savchenkoclass ProgramPoint: 6716236077SArtem Dergachev def __init__(self, json_pp): 68dd3c26a0STobias Hieta self.kind = json_pp["kind"] 69dd3c26a0STobias Hieta self.tag = json_pp["tag"] 70dd3c26a0STobias Hieta self.node_id = json_pp["node_id"] 71dd3c26a0STobias Hieta self.is_sink = bool(json_pp["is_sink"]) 72dd3c26a0STobias Hieta self.has_report = bool(json_pp["has_report"]) 73dd3c26a0STobias Hieta if self.kind == "Edge": 74dd3c26a0STobias Hieta self.src_id = json_pp["src_id"] 75dd3c26a0STobias Hieta self.dst_id = json_pp["dst_id"] 76dd3c26a0STobias Hieta elif self.kind == "Statement": 77ed035ff8SArtem Dergachev logging.debug(json_pp) 78dd3c26a0STobias Hieta self.stmt_kind = json_pp["stmt_kind"] 79dd3c26a0STobias Hieta self.cast_kind = json_pp["cast_kind"] if "cast_kind" in json_pp else None 80dd3c26a0STobias Hieta self.stmt_point_kind = json_pp["stmt_point_kind"] 81dd3c26a0STobias Hieta self.stmt_id = json_pp["stmt_id"] 82dd3c26a0STobias Hieta self.pointer = json_pp["pointer"] 83dd3c26a0STobias Hieta self.pretty = json_pp["pretty"] 84dd3c26a0STobias Hieta self.loc = ( 85dd3c26a0STobias Hieta SourceLocation(json_pp["location"]) 86dd3c26a0STobias Hieta if json_pp["location"] is not None 87dd3c26a0STobias Hieta else None 88dd3c26a0STobias Hieta ) 899cbf2dd6SBalazs Benics elif self.kind == "CallEnter": 909cbf2dd6SBalazs Benics self.callee_decl = json_pp.get("callee_decl", "None") 91dd3c26a0STobias Hieta elif self.kind == "BlockEntrance": 92dd3c26a0STobias Hieta self.block_id = json_pp["block_id"] 93*20cb4ec8SBalazs Benics elif self.kind == "PostInitializer": 94*20cb4ec8SBalazs Benics if "field_decl" in json_pp: 95*20cb4ec8SBalazs Benics self.target = json_pp["field_decl"] 96*20cb4ec8SBalazs Benics else: 97*20cb4ec8SBalazs Benics self.target = json_pp["type"] 9816236077SArtem Dergachev 9916236077SArtem Dergachev 1005740e77fSArtem Dergachev# A single expression acting as a key in a deserialized Environment. 101c98872e3SValeriy Savchenkoclass EnvironmentBindingKey: 1025740e77fSArtem Dergachev def __init__(self, json_ek): 1030a77d919SArtem Dergachev # CXXCtorInitializer is not a Stmt! 104dd3c26a0STobias Hieta self.stmt_id = ( 105dd3c26a0STobias Hieta json_ek["stmt_id"] if "stmt_id" in json_ek else json_ek["init_id"] 106dd3c26a0STobias Hieta ) 107dd3c26a0STobias Hieta self.pretty = json_ek["pretty"] 108dd3c26a0STobias Hieta self.kind = json_ek["kind"] if "kind" in json_ek else None 1095740e77fSArtem Dergachev 1105740e77fSArtem Dergachev def _key(self): 1115740e77fSArtem Dergachev return self.stmt_id 1125740e77fSArtem Dergachev 1135740e77fSArtem Dergachev def __eq__(self, other): 1145740e77fSArtem Dergachev return self._key() == other._key() 1155740e77fSArtem Dergachev 1165740e77fSArtem Dergachev def __hash__(self): 1175740e77fSArtem Dergachev return hash(self._key()) 11816236077SArtem Dergachev 11916236077SArtem Dergachev 12016236077SArtem Dergachev# Deserialized description of a location context. 121c98872e3SValeriy Savchenkoclass LocationContext: 12216236077SArtem Dergachev def __init__(self, json_frame): 123dd3c26a0STobias Hieta self.lctx_id = json_frame["lctx_id"] 124dd3c26a0STobias Hieta self.caption = json_frame["location_context"] 125dd3c26a0STobias Hieta self.decl = json_frame["calling"] 126dd3c26a0STobias Hieta self.loc = ( 127dd3c26a0STobias Hieta SourceLocation(json_frame["location"]) 128dd3c26a0STobias Hieta if json_frame["location"] is not None 129dd3c26a0STobias Hieta else None 130dd3c26a0STobias Hieta ) 13116236077SArtem Dergachev 1325740e77fSArtem Dergachev def _key(self): 1335740e77fSArtem Dergachev return self.lctx_id 1345740e77fSArtem Dergachev 1355740e77fSArtem Dergachev def __eq__(self, other): 1365740e77fSArtem Dergachev return self._key() == other._key() 1375740e77fSArtem Dergachev 1385740e77fSArtem Dergachev def __hash__(self): 1395740e77fSArtem Dergachev return hash(self._key()) 1405740e77fSArtem Dergachev 14116236077SArtem Dergachev 14216236077SArtem Dergachev# A group of deserialized Environment bindings that correspond to a specific 14316236077SArtem Dergachev# location context. 144c98872e3SValeriy Savchenkoclass EnvironmentFrame: 14516236077SArtem Dergachev def __init__(self, json_frame): 14616236077SArtem Dergachev self.location_context = LocationContext(json_frame) 1475740e77fSArtem Dergachev self.bindings = collections.OrderedDict( 148dd3c26a0STobias Hieta [(EnvironmentBindingKey(b), b["value"]) for b in json_frame["items"]] 149dd3c26a0STobias Hieta if json_frame["items"] is not None 150dd3c26a0STobias Hieta else [] 151dd3c26a0STobias Hieta ) 1525740e77fSArtem Dergachev 1535740e77fSArtem Dergachev def diff_bindings(self, prev): 1545740e77fSArtem Dergachev return diff_dicts(self.bindings, prev.bindings) 1555740e77fSArtem Dergachev 1565740e77fSArtem Dergachev def is_different(self, prev): 1575740e77fSArtem Dergachev removed, added = self.diff_bindings(prev) 1585740e77fSArtem Dergachev return len(removed) != 0 or len(added) != 0 15916236077SArtem Dergachev 16016236077SArtem Dergachev 1610a77d919SArtem Dergachev# A deserialized Environment. This class can also hold other entities that 162b032e3ffSisuckatcs# are similar to Environment, such as Objects Under Construction or 163b032e3ffSisuckatcs# Indices Of Elements Under Construction. 164c98872e3SValeriy Savchenkoclass GenericEnvironment: 16516236077SArtem Dergachev def __init__(self, json_e): 1660a77d919SArtem Dergachev self.frames = [EnvironmentFrame(f) for f in json_e] 16716236077SArtem Dergachev 1685740e77fSArtem Dergachev def diff_frames(self, prev): 1695740e77fSArtem Dergachev # TODO: It's difficult to display a good diff when frame numbers shift. 1705740e77fSArtem Dergachev if len(self.frames) != len(prev.frames): 1715740e77fSArtem Dergachev return None 17216236077SArtem Dergachev 1735740e77fSArtem Dergachev updated = [] 1745740e77fSArtem Dergachev for i in range(len(self.frames)): 1755740e77fSArtem Dergachev f = self.frames[i] 1765740e77fSArtem Dergachev prev_f = prev.frames[i] 1775740e77fSArtem Dergachev if f.location_context == prev_f.location_context: 1785740e77fSArtem Dergachev if f.is_different(prev_f): 1795740e77fSArtem Dergachev updated.append(i) 1805740e77fSArtem Dergachev else: 1815740e77fSArtem Dergachev # We have the whole frame replaced with another frame. 1825740e77fSArtem Dergachev # TODO: Produce a nice diff. 1835740e77fSArtem Dergachev return None 1845740e77fSArtem Dergachev 1855740e77fSArtem Dergachev # TODO: Add support for added/removed. 1865740e77fSArtem Dergachev return updated 1875740e77fSArtem Dergachev 1885740e77fSArtem Dergachev def is_different(self, prev): 1895740e77fSArtem Dergachev updated = self.diff_frames(prev) 1905740e77fSArtem Dergachev return updated is None or len(updated) > 0 1915740e77fSArtem Dergachev 1925740e77fSArtem Dergachev 1935740e77fSArtem Dergachev# A single binding key in a deserialized RegionStore cluster. 194c98872e3SValeriy Savchenkoclass StoreBindingKey: 1955740e77fSArtem Dergachev def __init__(self, json_sk): 196dd3c26a0STobias Hieta self.kind = json_sk["kind"] 197dd3c26a0STobias Hieta self.offset = json_sk["offset"] 1985740e77fSArtem Dergachev 1995740e77fSArtem Dergachev def _key(self): 2005740e77fSArtem Dergachev return (self.kind, self.offset) 2015740e77fSArtem Dergachev 2025740e77fSArtem Dergachev def __eq__(self, other): 2035740e77fSArtem Dergachev return self._key() == other._key() 2045740e77fSArtem Dergachev 2055740e77fSArtem Dergachev def __hash__(self): 2065740e77fSArtem Dergachev return hash(self._key()) 20716236077SArtem Dergachev 20816236077SArtem Dergachev 20916236077SArtem Dergachev# A single cluster of the deserialized RegionStore. 210c98872e3SValeriy Savchenkoclass StoreCluster: 21116236077SArtem Dergachev def __init__(self, json_sc): 212dd3c26a0STobias Hieta self.base_region = json_sc["cluster"] 2135740e77fSArtem Dergachev self.bindings = collections.OrderedDict( 214dd3c26a0STobias Hieta [(StoreBindingKey(b), b["value"]) for b in json_sc["items"]] 215dd3c26a0STobias Hieta ) 2165740e77fSArtem Dergachev 2175740e77fSArtem Dergachev def diff_bindings(self, prev): 2185740e77fSArtem Dergachev return diff_dicts(self.bindings, prev.bindings) 2195740e77fSArtem Dergachev 2205740e77fSArtem Dergachev def is_different(self, prev): 2215740e77fSArtem Dergachev removed, added = self.diff_bindings(prev) 2225740e77fSArtem Dergachev return len(removed) != 0 or len(added) != 0 22316236077SArtem Dergachev 22416236077SArtem Dergachev 22516236077SArtem Dergachev# A deserialized RegionStore. 226c98872e3SValeriy Savchenkoclass Store: 22716236077SArtem Dergachev def __init__(self, json_s): 228dd3c26a0STobias Hieta self.ptr = json_s["pointer"] 2295740e77fSArtem Dergachev self.clusters = collections.OrderedDict( 230dd3c26a0STobias Hieta [(c["pointer"], StoreCluster(c)) for c in json_s["items"]] 231dd3c26a0STobias Hieta ) 2325740e77fSArtem Dergachev 2335740e77fSArtem Dergachev def diff_clusters(self, prev): 2345740e77fSArtem Dergachev removed = [k for k in prev.clusters if k not in self.clusters] 2355740e77fSArtem Dergachev added = [k for k in self.clusters if k not in prev.clusters] 236dd3c26a0STobias Hieta updated = [ 237dd3c26a0STobias Hieta k 238dd3c26a0STobias Hieta for k in prev.clusters 239dd3c26a0STobias Hieta if k in self.clusters and prev.clusters[k].is_different(self.clusters[k]) 240dd3c26a0STobias Hieta ] 2415740e77fSArtem Dergachev return (removed, added, updated) 2425740e77fSArtem Dergachev 2435740e77fSArtem Dergachev def is_different(self, prev): 2445740e77fSArtem Dergachev removed, added, updated = self.diff_clusters(prev) 2455740e77fSArtem Dergachev return len(removed) != 0 or len(added) != 0 or len(updated) != 0 24616236077SArtem Dergachev 24716236077SArtem Dergachev 248deb7accbSArtem Dergachev# Deserialized messages from a single checker in a single program state. 249deb7accbSArtem Dergachev# Basically a list of raw strings. 250c98872e3SValeriy Savchenkoclass CheckerLines: 251deb7accbSArtem Dergachev def __init__(self, json_lines): 252deb7accbSArtem Dergachev self.lines = json_lines 253deb7accbSArtem Dergachev 254deb7accbSArtem Dergachev def diff_lines(self, prev): 255deb7accbSArtem Dergachev lines = difflib.ndiff(prev.lines, self.lines) 256dd3c26a0STobias Hieta return [l.strip() for l in lines if l.startswith("+") or l.startswith("-")] 257deb7accbSArtem Dergachev 258deb7accbSArtem Dergachev def is_different(self, prev): 259deb7accbSArtem Dergachev return len(self.diff_lines(prev)) > 0 260deb7accbSArtem Dergachev 261deb7accbSArtem Dergachev 262deb7accbSArtem Dergachev# Deserialized messages of all checkers, separated by checker. 263c98872e3SValeriy Savchenkoclass CheckerMessages: 264deb7accbSArtem Dergachev def __init__(self, json_m): 265deb7accbSArtem Dergachev self.items = collections.OrderedDict( 266dd3c26a0STobias Hieta [(m["checker"], CheckerLines(m["messages"])) for m in json_m] 267dd3c26a0STobias Hieta ) 268deb7accbSArtem Dergachev 269deb7accbSArtem Dergachev def diff_messages(self, prev): 270deb7accbSArtem Dergachev removed = [k for k in prev.items if k not in self.items] 271deb7accbSArtem Dergachev added = [k for k in self.items if k not in prev.items] 272dd3c26a0STobias Hieta updated = [ 273dd3c26a0STobias Hieta k 274dd3c26a0STobias Hieta for k in prev.items 275dd3c26a0STobias Hieta if k in self.items and prev.items[k].is_different(self.items[k]) 276dd3c26a0STobias Hieta ] 277deb7accbSArtem Dergachev return (removed, added, updated) 278deb7accbSArtem Dergachev 279deb7accbSArtem Dergachev def is_different(self, prev): 280deb7accbSArtem Dergachev removed, added, updated = self.diff_messages(prev) 281deb7accbSArtem Dergachev return len(removed) != 0 or len(added) != 0 or len(updated) != 0 282deb7accbSArtem Dergachev 283deb7accbSArtem Dergachev 28416236077SArtem Dergachev# A deserialized program state. 285c98872e3SValeriy Savchenkoclass ProgramState: 28616236077SArtem Dergachev def __init__(self, state_id, json_ps): 287dd3c26a0STobias Hieta logging.debug("Adding ProgramState " + str(state_id)) 28816236077SArtem Dergachev 289dd3c26a0STobias Hieta store_key = "store" 290dd3c26a0STobias Hieta env_key = "environment" 291dd3c26a0STobias Hieta constraints_key = "constraints" 292dd3c26a0STobias Hieta dyn_ty_key = "dynamic_types" 293dd3c26a0STobias Hieta ctor_key = "constructing_objects" 294dd3c26a0STobias Hieta ind_key = "index_of_element" 295dd3c26a0STobias Hieta init_loop_key = "pending_init_loops" 296dd3c26a0STobias Hieta dtor_key = "pending_destructors" 297dd3c26a0STobias Hieta msg_key = "checker_messages" 298b5147937Sisuckatcs 299d93b810cSArtem Dergachev if json_ps is None: 300d93b810cSArtem Dergachev json_ps = { 301b5147937Sisuckatcs store_key: None, 302b5147937Sisuckatcs env_key: None, 303b5147937Sisuckatcs constraints_key: None, 304b5147937Sisuckatcs dyn_ty_key: None, 305b5147937Sisuckatcs ctor_key: None, 306b5147937Sisuckatcs ind_key: None, 307b5147937Sisuckatcs init_loop_key: None, 308b5147937Sisuckatcs dtor_key: None, 309dd3c26a0STobias Hieta msg_key: None, 310d93b810cSArtem Dergachev } 311d93b810cSArtem Dergachev 31216236077SArtem Dergachev self.state_id = state_id 3130a77d919SArtem Dergachev 314dd3c26a0STobias Hieta self.store = ( 315dd3c26a0STobias Hieta Store(json_ps[store_key]) if json_ps[store_key] is not None else None 316dd3c26a0STobias Hieta ) 3170a77d919SArtem Dergachev 318dd3c26a0STobias Hieta self.environment = ( 319dd3c26a0STobias Hieta GenericEnvironment(json_ps[env_key]["items"]) 320dd3c26a0STobias Hieta if json_ps[env_key] is not None 321dd3c26a0STobias Hieta else None 322dd3c26a0STobias Hieta ) 3230a77d919SArtem Dergachev 324dd3c26a0STobias Hieta self.constraints = ( 325dd3c26a0STobias Hieta GenericMap([(c["symbol"], c["range"]) for c in json_ps[constraints_key]]) 326dd3c26a0STobias Hieta if json_ps[constraints_key] is not None 327dd3c26a0STobias Hieta else None 328dd3c26a0STobias Hieta ) 3290a77d919SArtem Dergachev 330dd3c26a0STobias Hieta self.dynamic_types = ( 331dd3c26a0STobias Hieta GenericMap( 332dd3c26a0STobias Hieta [ 333dd3c26a0STobias Hieta ( 334dd3c26a0STobias Hieta t["region"], 335dd3c26a0STobias Hieta "%s%s" 336dd3c26a0STobias Hieta % ( 337dd3c26a0STobias Hieta t["dyn_type"], 338dd3c26a0STobias Hieta " (or a sub-class)" if t["sub_classable"] else "", 339dd3c26a0STobias Hieta ), 340dd3c26a0STobias Hieta ) 341dd3c26a0STobias Hieta for t in json_ps[dyn_ty_key] 342dd3c26a0STobias Hieta ] 343dd3c26a0STobias Hieta ) 344dd3c26a0STobias Hieta if json_ps[dyn_ty_key] is not None 345dd3c26a0STobias Hieta else None 346dd3c26a0STobias Hieta ) 347b5147937Sisuckatcs 348dd3c26a0STobias Hieta self.checker_messages = ( 349dd3c26a0STobias Hieta CheckerMessages(json_ps[msg_key]) if json_ps[msg_key] is not None else None 350dd3c26a0STobias Hieta ) 351b5147937Sisuckatcs 352b5147937Sisuckatcs # State traits 353b5147937Sisuckatcs # 354b5147937Sisuckatcs # For traits we always check if a key exists because if a trait 355b5147937Sisuckatcs # has no imformation, nothing will be printed in the .dot file 356b5147937Sisuckatcs # we parse. 35702f91ddfSArtem Dergachev 358dd3c26a0STobias Hieta self.constructing_objects = ( 359dd3c26a0STobias Hieta GenericEnvironment(json_ps[ctor_key]) 360dd3c26a0STobias Hieta if ctor_key in json_ps and json_ps[ctor_key] is not None 361dd3c26a0STobias Hieta else None 362dd3c26a0STobias Hieta ) 3630a77d919SArtem Dergachev 364dd3c26a0STobias Hieta self.index_of_element = ( 365dd3c26a0STobias Hieta GenericEnvironment(json_ps[ind_key]) 366dd3c26a0STobias Hieta if ind_key in json_ps and json_ps[ind_key] is not None 367dd3c26a0STobias Hieta else None 368dd3c26a0STobias Hieta ) 369b032e3ffSisuckatcs 370dd3c26a0STobias Hieta self.pending_init_loops = ( 371dd3c26a0STobias Hieta GenericEnvironment(json_ps[init_loop_key]) 372dd3c26a0STobias Hieta if init_loop_key in json_ps and json_ps[init_loop_key] is not None 373dd3c26a0STobias Hieta else None 374dd3c26a0STobias Hieta ) 375b5147937Sisuckatcs 376dd3c26a0STobias Hieta self.pending_destructors = ( 377dd3c26a0STobias Hieta GenericEnvironment(json_ps[dtor_key]) 378dd3c26a0STobias Hieta if dtor_key in json_ps and json_ps[dtor_key] is not None 379dd3c26a0STobias Hieta else None 380dd3c26a0STobias Hieta ) 38116236077SArtem Dergachev 38216236077SArtem Dergachev 38316236077SArtem Dergachev# A deserialized exploded graph node. Has a default constructor because it 38416236077SArtem Dergachev# may be referenced as part of an edge before its contents are deserialized, 38516236077SArtem Dergachev# and in this moment we already need a room for predecessors and successors. 386c98872e3SValeriy Savchenkoclass ExplodedNode: 38716236077SArtem Dergachev def __init__(self): 38816236077SArtem Dergachev self.predecessors = [] 38916236077SArtem Dergachev self.successors = [] 39016236077SArtem Dergachev 39116236077SArtem Dergachev def construct(self, node_id, json_node): 392dd3c26a0STobias Hieta logging.debug("Adding " + node_id) 39314e9eb3dSArtem Dergachev self.ptr = node_id[4:] 394dd3c26a0STobias Hieta self.points = [ProgramPoint(p) for p in json_node["program_points"]] 39514e9eb3dSArtem Dergachev self.node_id = self.points[-1].node_id 396dd3c26a0STobias Hieta self.state = ProgramState( 397dd3c26a0STobias Hieta json_node["state_id"], 398dd3c26a0STobias Hieta json_node["program_state"] 399dd3c26a0STobias Hieta if json_node["program_state"] is not None 400dd3c26a0STobias Hieta else None, 401dd3c26a0STobias Hieta ) 40216236077SArtem Dergachev 40316236077SArtem Dergachev assert self.node_name() == node_id 40416236077SArtem Dergachev 40516236077SArtem Dergachev def node_name(self): 406dd3c26a0STobias Hieta return "Node" + self.ptr 40716236077SArtem Dergachev 40816236077SArtem Dergachev 40916236077SArtem Dergachev# A deserialized ExplodedGraph. Constructed by consuming a .dot file 41016236077SArtem Dergachev# line-by-line. 411c98872e3SValeriy Savchenkoclass ExplodedGraph: 41216236077SArtem Dergachev # Parse .dot files with regular expressions. 41316236077SArtem Dergachev node_re = re.compile( 414dd3c26a0STobias Hieta '^(Node0x[0-9a-f]*) \\[shape=record,.*label="{(.*)\\\\l}"\\];$' 415dd3c26a0STobias Hieta ) 416dd3c26a0STobias Hieta edge_re = re.compile("^(Node0x[0-9a-f]*) -> (Node0x[0-9a-f]*);$") 41716236077SArtem Dergachev 41816236077SArtem Dergachev def __init__(self): 41916236077SArtem Dergachev self.nodes = collections.defaultdict(ExplodedNode) 42016236077SArtem Dergachev self.root_id = None 421dd3c26a0STobias Hieta self.incomplete_line = "" 42216236077SArtem Dergachev 42316236077SArtem Dergachev def add_raw_line(self, raw_line): 424dd3c26a0STobias Hieta if raw_line.startswith("//"): 42516236077SArtem Dergachev return 42616236077SArtem Dergachev 42716236077SArtem Dergachev # Allow line breaks by waiting for ';'. This is not valid in 42816236077SArtem Dergachev # a .dot file, but it is useful for writing tests. 429dd3c26a0STobias Hieta if len(raw_line) > 0 and raw_line[-1] != ";": 43016236077SArtem Dergachev self.incomplete_line += raw_line 43116236077SArtem Dergachev return 43216236077SArtem Dergachev raw_line = self.incomplete_line + raw_line 433dd3c26a0STobias Hieta self.incomplete_line = "" 43416236077SArtem Dergachev 43516236077SArtem Dergachev # Apply regexps one by one to see if it's a node or an edge 43616236077SArtem Dergachev # and extract contents if necessary. 437dd3c26a0STobias Hieta logging.debug("Line: " + raw_line) 43816236077SArtem Dergachev result = self.edge_re.match(raw_line) 43916236077SArtem Dergachev if result is not None: 440dd3c26a0STobias Hieta logging.debug("Classified as edge line.") 44116236077SArtem Dergachev pred = result.group(1) 44216236077SArtem Dergachev succ = result.group(2) 44316236077SArtem Dergachev self.nodes[pred].successors.append(succ) 44416236077SArtem Dergachev self.nodes[succ].predecessors.append(pred) 44516236077SArtem Dergachev return 44616236077SArtem Dergachev result = self.node_re.match(raw_line) 44716236077SArtem Dergachev if result is not None: 448dd3c26a0STobias Hieta logging.debug("Classified as node line.") 44916236077SArtem Dergachev node_id = result.group(1) 45016236077SArtem Dergachev if len(self.nodes) == 0: 45116236077SArtem Dergachev self.root_id = node_id 45216236077SArtem Dergachev # Note: when writing tests you don't need to escape everything, 45316236077SArtem Dergachev # even though in a valid dot file everything is escaped. 454dd3c26a0STobias Hieta node_label = ( 455dd3c26a0STobias Hieta result.group(2) 456dd3c26a0STobias Hieta .replace(" ", "") 457dd3c26a0STobias Hieta .replace('\\"', '"') 458dd3c26a0STobias Hieta .replace("\\{", "{") 459dd3c26a0STobias Hieta .replace("\\}", "}") 460dd3c26a0STobias Hieta .replace("\\\\", "\\") 461dd3c26a0STobias Hieta .replace("\\|", "|") 462dd3c26a0STobias Hieta .replace("\\<", "\\\\<") 463dd3c26a0STobias Hieta .replace("\\>", "\\\\>") 464dd3c26a0STobias Hieta .rstrip(",") 465dd3c26a0STobias Hieta ) 46601f9388dSDenys Petrov # Handle `\l` separately because a string literal can be in code 46701f9388dSDenys Petrov # like "string\\literal" with the `\l` inside. 46801f9388dSDenys Petrov # Also on Windows macros __FILE__ produces specific delimiters `\` 46901f9388dSDenys Petrov # and a directory or file may starts with the letter `l`. 47001f9388dSDenys Petrov # Find all `\l` (like `,\l`, `}\l`, `[\l`) except `\\l`, 4715674a3c8SGabriel Ravier # because the literal as a rule contains multiple `\` before `\l`. 472dd3c26a0STobias Hieta node_label = re.sub(r"(?<!\\)\\l", "", node_label) 47316236077SArtem Dergachev logging.debug(node_label) 47416236077SArtem Dergachev json_node = json.loads(node_label) 47516236077SArtem Dergachev self.nodes[node_id].construct(node_id, json_node) 47616236077SArtem Dergachev return 477dd3c26a0STobias Hieta logging.debug("Skipping.") 47816236077SArtem Dergachev 47916236077SArtem Dergachev 4805fcf92e1SArtem Dergachev# ===-----------------------------------------------------------------------===# 4815fcf92e1SArtem Dergachev# Visitors traverse a deserialized ExplodedGraph and do different things 4825fcf92e1SArtem Dergachev# with every node and edge. 4835fcf92e1SArtem Dergachev# ===-----------------------------------------------------------------------===# 4845fcf92e1SArtem Dergachev 4855fcf92e1SArtem Dergachev 48616236077SArtem Dergachev# A visitor that dumps the ExplodedGraph into a DOT file with fancy HTML-based 48716236077SArtem Dergachev# syntax highlighing. 488c98872e3SValeriy Savchenkoclass DotDumpVisitor: 4890c3e24f7SElla Ma def __init__( 4900c3e24f7SElla Ma self, do_diffs, dark_mode, gray_mode, topo_mode, dump_html_only, dump_dot_only 4910c3e24f7SElla Ma ): 4920c3e24f7SElla Ma assert not (dump_html_only and dump_dot_only), ( 4930c3e24f7SElla Ma "Option dump_html_only and dump_dot_only are conflict, " 4940c3e24f7SElla Ma "they cannot be true at the same time." 4950c3e24f7SElla Ma ) 4960c3e24f7SElla Ma 4975740e77fSArtem Dergachev self._do_diffs = do_diffs 498ad38e58eSArtem Dergachev self._dark_mode = dark_mode 49978c0aefbSArtem Dergachev self._gray_mode = gray_mode 500c6b5c5b9SArtem Dergachev self._topo_mode = topo_mode 5010c3e24f7SElla Ma self._dump_html_only = dump_html_only 502e9e36354SArtem Dergachev self._dump_dot_only = dump_dot_only 503e9e36354SArtem Dergachev self._output = [] 50416236077SArtem Dergachev 505e9e36354SArtem Dergachev def _dump_raw(self, s): 506e9e36354SArtem Dergachev if self._dump_dot_only: 507dd3c26a0STobias Hieta print(s, end="") 508e9e36354SArtem Dergachev else: 509e9e36354SArtem Dergachev self._output.append(s) 510e9e36354SArtem Dergachev 511e9e36354SArtem Dergachev def output(self): 512e9e36354SArtem Dergachev assert not self._dump_dot_only 513dd3c26a0STobias Hieta return "".join(self._output) 51416236077SArtem Dergachev 51578c0aefbSArtem Dergachev def _dump(self, s): 516dd3c26a0STobias Hieta s = ( 517dd3c26a0STobias Hieta s.replace("&", "&") 518dd3c26a0STobias Hieta .replace("{", "\\{") 519dd3c26a0STobias Hieta .replace("}", "\\}") 520dd3c26a0STobias Hieta .replace("\\<", "<") 521dd3c26a0STobias Hieta .replace("\\>", ">") 522dd3c26a0STobias Hieta .replace("|", "\\|") 523dd3c26a0STobias Hieta ) 524dd3c26a0STobias Hieta s = re.sub(r"(?<!\\)\\l", "<br />", s) 52578c0aefbSArtem Dergachev if self._gray_mode: 526dd3c26a0STobias Hieta s = re.sub(r'<font color="[a-z0-9]*">', "", s) 527dd3c26a0STobias Hieta s = re.sub(r"</font>", "", s) 52878c0aefbSArtem Dergachev self._dump_raw(s) 52916236077SArtem Dergachev 5305740e77fSArtem Dergachev @staticmethod 5315740e77fSArtem Dergachev def _diff_plus_minus(is_added): 5325740e77fSArtem Dergachev if is_added is None: 533dd3c26a0STobias Hieta return "" 5345740e77fSArtem Dergachev if is_added: 5355740e77fSArtem Dergachev return '<font color="forestgreen">+</font>' 5365740e77fSArtem Dergachev return '<font color="red">-</font>' 5375740e77fSArtem Dergachev 53848a5c83aSArtem Dergachev @staticmethod 53948a5c83aSArtem Dergachev def _short_pretty(s): 54048a5c83aSArtem Dergachev if s is None: 54148a5c83aSArtem Dergachev return None 54248a5c83aSArtem Dergachev if len(s) < 20: 54348a5c83aSArtem Dergachev return s 544dd3c26a0STobias Hieta left = s.find("{") 545dd3c26a0STobias Hieta right = s.rfind("}") 54648a5c83aSArtem Dergachev if left == -1 or right == -1 or left >= right: 54748a5c83aSArtem Dergachev return s 548dd3c26a0STobias Hieta candidate = s[0 : left + 1] + " ... " + s[right:] 54948a5c83aSArtem Dergachev if len(candidate) >= len(s): 55048a5c83aSArtem Dergachev return s 55148a5c83aSArtem Dergachev return candidate 55248a5c83aSArtem Dergachev 553ed035ff8SArtem Dergachev @staticmethod 554ed035ff8SArtem Dergachev def _make_sloc(loc): 555ed035ff8SArtem Dergachev if loc is None: 556dd3c26a0STobias Hieta return "<i>Invalid Source Location</i>" 557ed035ff8SArtem Dergachev 558ed035ff8SArtem Dergachev def make_plain_loc(loc): 559dd3c26a0STobias Hieta return "%s:<b>%s</b>:<b>%s</b>" % (loc.filename, loc.line, loc.col) 560ed035ff8SArtem Dergachev 561ed035ff8SArtem Dergachev if loc.is_macro(): 562dd3c26a0STobias Hieta return '%s <font color="royalblue1">' "(<i>spelling at </i> %s)</font>" % ( 563dd3c26a0STobias Hieta make_plain_loc(loc), 564dd3c26a0STobias Hieta make_plain_loc(loc.spelling), 565dd3c26a0STobias Hieta ) 566ed035ff8SArtem Dergachev 567ed035ff8SArtem Dergachev return make_plain_loc(loc) 568ed035ff8SArtem Dergachev 56916236077SArtem Dergachev def visit_begin_graph(self, graph): 57016236077SArtem Dergachev self._graph = graph 57116236077SArtem Dergachev self._dump_raw('digraph "ExplodedGraph" {\n') 572ad38e58eSArtem Dergachev if self._dark_mode: 573ad38e58eSArtem Dergachev self._dump_raw('bgcolor="gray10";\n') 57416236077SArtem Dergachev self._dump_raw('label="";\n') 57516236077SArtem Dergachev 57616236077SArtem Dergachev def visit_program_point(self, p): 577dd3c26a0STobias Hieta if p.kind in ["Edge", "BlockEntrance", "BlockExit"]: 578dd3c26a0STobias Hieta color = "gold3" 579dd3c26a0STobias Hieta elif p.kind in ["PreStmtPurgeDeadSymbols", "PostStmtPurgeDeadSymbols"]: 580dd3c26a0STobias Hieta color = "red" 581dd3c26a0STobias Hieta elif p.kind in ["CallEnter", "CallExitBegin", "CallExitEnd"]: 582dd3c26a0STobias Hieta color = "dodgerblue" if self._dark_mode else "blue" 583dd3c26a0STobias Hieta elif p.kind in ["Statement"]: 584dd3c26a0STobias Hieta color = "cyan4" 58516236077SArtem Dergachev else: 586dd3c26a0STobias Hieta color = "forestgreen" 58716236077SArtem Dergachev 58814e9eb3dSArtem Dergachev self._dump('<tr><td align="left">%s.</td>' % p.node_id) 58914e9eb3dSArtem Dergachev 590dd3c26a0STobias Hieta if p.kind == "Statement": 5912ca53557SArtem Dergachev # This avoids pretty-printing huge statements such as CompoundStmt. 5922ca53557SArtem Dergachev # Such statements show up only at [Pre|Post]StmtPurgeDeadSymbols 593dd3c26a0STobias Hieta skip_pretty = "PurgeDeadSymbols" in p.stmt_point_kind 594dd3c26a0STobias Hieta stmt_color = "cyan3" 595dd3c26a0STobias Hieta self._dump( 596dd3c26a0STobias Hieta '<td align="left" width="0">%s:</td>' 59716236077SArtem Dergachev '<td align="left" width="0"><font color="%s">' 598dd3c26a0STobias Hieta "%s</font> </td>" 599ed035ff8SArtem Dergachev '<td align="left"><i>S%s</i></td>' 6002ca53557SArtem Dergachev '<td align="left"><font color="%s">%s</font></td>' 601ed035ff8SArtem Dergachev '<td align="left">%s</td></tr>' 602dd3c26a0STobias Hieta % ( 603dd3c26a0STobias Hieta self._make_sloc(p.loc), 604dd3c26a0STobias Hieta color, 605dd3c26a0STobias Hieta "%s (%s)" % (p.stmt_kind, p.cast_kind) 606dd3c26a0STobias Hieta if p.cast_kind is not None 607dd3c26a0STobias Hieta else p.stmt_kind, 608dd3c26a0STobias Hieta p.stmt_id, 609dd3c26a0STobias Hieta stmt_color, 610dd3c26a0STobias Hieta p.stmt_point_kind, 611dd3c26a0STobias Hieta self._short_pretty(p.pretty) if not skip_pretty else "", 612dd3c26a0STobias Hieta ) 613dd3c26a0STobias Hieta ) 614dd3c26a0STobias Hieta elif p.kind == "Edge": 615dd3c26a0STobias Hieta self._dump( 616dd3c26a0STobias Hieta '<td width="0"></td>' 61716236077SArtem Dergachev '<td align="left" width="0">' 61816236077SArtem Dergachev '<font color="%s">%s</font></td><td align="left">' 619dd3c26a0STobias Hieta "[B%d] -\\> [B%d]</td></tr>" % (color, "BlockEdge", p.src_id, p.dst_id) 620dd3c26a0STobias Hieta ) 621dd3c26a0STobias Hieta elif p.kind == "BlockEntrance": 622dd3c26a0STobias Hieta self._dump( 623dd3c26a0STobias Hieta '<td width="0"></td>' 6242ca53557SArtem Dergachev '<td align="left" width="0">' 6252ca53557SArtem Dergachev '<font color="%s">%s</font></td>' 626dd3c26a0STobias Hieta '<td align="left">[B%d]</td></tr>' % (color, p.kind, p.block_id) 627dd3c26a0STobias Hieta ) 6289cbf2dd6SBalazs Benics elif p.kind == "CallEnter": 6299cbf2dd6SBalazs Benics self._dump( 6309cbf2dd6SBalazs Benics '<td width="0"></td>' 6319cbf2dd6SBalazs Benics '<td align="left" width="0">' 6329cbf2dd6SBalazs Benics '<font color="%s">%s</font></td>' 6339cbf2dd6SBalazs Benics '<td align="left">%s</td></tr>' % (color, p.kind, p.callee_decl) 6349cbf2dd6SBalazs Benics ) 635*20cb4ec8SBalazs Benics elif p.kind == "PostInitializer": 636*20cb4ec8SBalazs Benics self._dump( 637*20cb4ec8SBalazs Benics '<td width="0"></td>' 638*20cb4ec8SBalazs Benics '<td align="left" width="0">' 639*20cb4ec8SBalazs Benics '<font color="%s">%s</font></td>' 640*20cb4ec8SBalazs Benics '<td align="left">%s</td></tr>' % (color, p.kind, p.target) 641*20cb4ec8SBalazs Benics ) 64216236077SArtem Dergachev else: 64316236077SArtem Dergachev # TODO: Print more stuff for other kinds of points. 644dd3c26a0STobias Hieta self._dump( 645dd3c26a0STobias Hieta '<td width="0"></td>' 64616236077SArtem Dergachev '<td align="left" width="0" colspan="2">' 647dd3c26a0STobias Hieta '<font color="%s">%s</font></td></tr>' % (color, p.kind) 648dd3c26a0STobias Hieta ) 64916236077SArtem Dergachev 6505a72338bSArtem Dergachev if p.tag is not None: 651dd3c26a0STobias Hieta self._dump( 652dd3c26a0STobias Hieta '<tr><td width="0"></td><td width="0"></td>' 6532ca53557SArtem Dergachev '<td colspan="3" align="left">' 6545a72338bSArtem Dergachev '<b>Tag: </b> <font color="crimson">' 655dd3c26a0STobias Hieta "%s</font></td></tr>" % p.tag 656dd3c26a0STobias Hieta ) 6575a72338bSArtem Dergachev 65814e9eb3dSArtem Dergachev if p.has_report: 659dd3c26a0STobias Hieta self._dump( 660dd3c26a0STobias Hieta '<tr><td width="0"></td><td width="0"></td>' 66114e9eb3dSArtem Dergachev '<td colspan="3" align="left">' 66214e9eb3dSArtem Dergachev '<font color="red"><b>Bug Report Attached' 663dd3c26a0STobias Hieta "</b></font></td></tr>" 664dd3c26a0STobias Hieta ) 66514e9eb3dSArtem Dergachev if p.is_sink: 666dd3c26a0STobias Hieta self._dump( 667dd3c26a0STobias Hieta '<tr><td width="0"></td><td width="0"></td>' 66814e9eb3dSArtem Dergachev '<td colspan="3" align="left">' 66914e9eb3dSArtem Dergachev '<font color="cornflowerblue"><b>Sink Node' 670dd3c26a0STobias Hieta "</b></font></td></tr>" 671dd3c26a0STobias Hieta ) 67214e9eb3dSArtem Dergachev 6735740e77fSArtem Dergachev def visit_environment(self, e, prev_e=None): 67416236077SArtem Dergachev self._dump('<table border="0">') 67516236077SArtem Dergachev 6765740e77fSArtem Dergachev def dump_location_context(lc, is_added=None): 677dd3c26a0STobias Hieta self._dump( 678dd3c26a0STobias Hieta "<tr><td>%s</td>" 6795740e77fSArtem Dergachev '<td align="left"><b>%s</b></td>' 680628f36ffSArtem Dergachev '<td align="left" colspan="2">' 681ad38e58eSArtem Dergachev '<font color="gray60">%s </font>' 682dd3c26a0STobias Hieta "%s</td></tr>" 683dd3c26a0STobias Hieta % ( 684dd3c26a0STobias Hieta self._diff_plus_minus(is_added), 685dd3c26a0STobias Hieta lc.caption, 686dd3c26a0STobias Hieta lc.decl, 687dd3c26a0STobias Hieta ("(%s)" % self._make_sloc(lc.loc)) if lc.loc is not None else "", 688dd3c26a0STobias Hieta ) 689dd3c26a0STobias Hieta ) 6905740e77fSArtem Dergachev 6915740e77fSArtem Dergachev def dump_binding(f, b, is_added=None): 692dd3c26a0STobias Hieta self._dump( 693dd3c26a0STobias Hieta "<tr><td>%s</td>" 6945740e77fSArtem Dergachev '<td align="left"><i>S%s</i></td>' 695dd3c26a0STobias Hieta "%s" 69616236077SArtem Dergachev '<td align="left">%s</td>' 69716236077SArtem Dergachev '<td align="left">%s</td></tr>' 698dd3c26a0STobias Hieta % ( 699dd3c26a0STobias Hieta self._diff_plus_minus(is_added), 7000a77d919SArtem Dergachev b.stmt_id, 701ad38e58eSArtem Dergachev '<td align="left"><font color="%s"><i>' 702dd3c26a0STobias Hieta "%s</i></font></td>" 703dd3c26a0STobias Hieta % ( 704dd3c26a0STobias Hieta "lavender" if self._dark_mode else "darkgreen", 705dd3c26a0STobias Hieta ("(%s)" % b.kind) if b.kind is not None else " ", 706ad38e58eSArtem Dergachev ), 707dd3c26a0STobias Hieta self._short_pretty(b.pretty), 708dd3c26a0STobias Hieta f.bindings[b], 709dd3c26a0STobias Hieta ) 710dd3c26a0STobias Hieta ) 7115740e77fSArtem Dergachev 7125740e77fSArtem Dergachev frames_updated = e.diff_frames(prev_e) if prev_e is not None else None 7135740e77fSArtem Dergachev if frames_updated: 7145740e77fSArtem Dergachev for i in frames_updated: 7155740e77fSArtem Dergachev f = e.frames[i] 7165740e77fSArtem Dergachev prev_f = prev_e.frames[i] 7175740e77fSArtem Dergachev dump_location_context(f.location_context) 7185740e77fSArtem Dergachev bindings_removed, bindings_added = f.diff_bindings(prev_f) 7195740e77fSArtem Dergachev for b in bindings_removed: 7205740e77fSArtem Dergachev dump_binding(prev_f, b, False) 7215740e77fSArtem Dergachev for b in bindings_added: 7225740e77fSArtem Dergachev dump_binding(f, b, True) 7235740e77fSArtem Dergachev else: 7245740e77fSArtem Dergachev for f in e.frames: 7255740e77fSArtem Dergachev dump_location_context(f.location_context) 7265740e77fSArtem Dergachev for b in f.bindings: 7275740e77fSArtem Dergachev dump_binding(f, b) 72816236077SArtem Dergachev 729dd3c26a0STobias Hieta self._dump("</table>") 73016236077SArtem Dergachev 7310a77d919SArtem Dergachev def visit_environment_in_state(self, selector, title, s, prev_s=None): 7320a77d919SArtem Dergachev e = getattr(s, selector) 7330a77d919SArtem Dergachev prev_e = getattr(prev_s, selector) if prev_s is not None else None 7340a77d919SArtem Dergachev if e is None and prev_e is None: 7350a77d919SArtem Dergachev return 7360a77d919SArtem Dergachev 7370a77d919SArtem Dergachev self._dump('<hr /><tr><td align="left"><b>%s: </b>' % title) 7380a77d919SArtem Dergachev if e is None: 739dd3c26a0STobias Hieta self._dump("<i> Nothing!</i>") 740b9c94f94SArtem Dergachev else: 7410a77d919SArtem Dergachev if prev_e is not None: 7420a77d919SArtem Dergachev if e.is_different(prev_e): 743b9c94f94SArtem Dergachev self._dump('</td></tr><tr><td align="left">') 7440a77d919SArtem Dergachev self.visit_environment(e, prev_e) 745b9c94f94SArtem Dergachev else: 746dd3c26a0STobias Hieta self._dump("<i> No changes!</i>") 747b9c94f94SArtem Dergachev else: 748b9c94f94SArtem Dergachev self._dump('</td></tr><tr><td align="left">') 7490a77d919SArtem Dergachev self.visit_environment(e) 750b9c94f94SArtem Dergachev 751dd3c26a0STobias Hieta self._dump("</td></tr>") 752b9c94f94SArtem Dergachev 7535740e77fSArtem Dergachev def visit_store(self, s, prev_s=None): 75416236077SArtem Dergachev self._dump('<table border="0">') 75516236077SArtem Dergachev 7565740e77fSArtem Dergachev def dump_binding(s, c, b, is_added=None): 757dd3c26a0STobias Hieta self._dump( 758dd3c26a0STobias Hieta "<tr><td>%s</td>" 7595740e77fSArtem Dergachev '<td align="left">%s</td>' 76016236077SArtem Dergachev '<td align="left">%s</td>' 76116236077SArtem Dergachev '<td align="left">%s</td>' 76216236077SArtem Dergachev '<td align="left">%s</td></tr>' 763dd3c26a0STobias Hieta % ( 764dd3c26a0STobias Hieta self._diff_plus_minus(is_added), 765dd3c26a0STobias Hieta s.clusters[c].base_region, 766dd3c26a0STobias Hieta b.offset, 767dd3c26a0STobias Hieta "(<i>Default</i>)" if b.kind == "Default" else "", 768dd3c26a0STobias Hieta s.clusters[c].bindings[b], 769dd3c26a0STobias Hieta ) 770dd3c26a0STobias Hieta ) 7715740e77fSArtem Dergachev 7725740e77fSArtem Dergachev if prev_s is not None: 773dd3c26a0STobias Hieta clusters_removed, clusters_added, clusters_updated = s.diff_clusters(prev_s) 7745740e77fSArtem Dergachev for c in clusters_removed: 7755740e77fSArtem Dergachev for b in prev_s.clusters[c].bindings: 7765740e77fSArtem Dergachev dump_binding(prev_s, c, b, False) 7775740e77fSArtem Dergachev for c in clusters_updated: 778dd3c26a0STobias Hieta bindings_removed, bindings_added = s.clusters[c].diff_bindings( 779dd3c26a0STobias Hieta prev_s.clusters[c] 780dd3c26a0STobias Hieta ) 7815740e77fSArtem Dergachev for b in bindings_removed: 7825740e77fSArtem Dergachev dump_binding(prev_s, c, b, False) 7835740e77fSArtem Dergachev for b in bindings_added: 7845740e77fSArtem Dergachev dump_binding(s, c, b, True) 7855740e77fSArtem Dergachev for c in clusters_added: 7865740e77fSArtem Dergachev for b in s.clusters[c].bindings: 7875740e77fSArtem Dergachev dump_binding(s, c, b, True) 7885740e77fSArtem Dergachev else: 7895740e77fSArtem Dergachev for c in s.clusters: 7905740e77fSArtem Dergachev for b in s.clusters[c].bindings: 7915740e77fSArtem Dergachev dump_binding(s, c, b) 79216236077SArtem Dergachev 793dd3c26a0STobias Hieta self._dump("</table>") 79416236077SArtem Dergachev 795b9c94f94SArtem Dergachev def visit_store_in_state(self, s, prev_s=None): 7960a77d919SArtem Dergachev st = s.store 7970a77d919SArtem Dergachev prev_st = prev_s.store if prev_s is not None else None 7980a77d919SArtem Dergachev if st is None and prev_st is None: 7990a77d919SArtem Dergachev return 8000a77d919SArtem Dergachev 80102f91ddfSArtem Dergachev self._dump('<hr /><tr><td align="left"><b>Store: </b>') 8020a77d919SArtem Dergachev if st is None: 803dd3c26a0STobias Hieta self._dump("<i> Nothing!</i>") 80416236077SArtem Dergachev else: 805daf41722SArtem Dergachev if self._dark_mode: 806daf41722SArtem Dergachev self._dump(' <font color="gray30">(%s)</font>' % st.ptr) 807daf41722SArtem Dergachev else: 808daf41722SArtem Dergachev self._dump(' <font color="gray">(%s)</font>' % st.ptr) 8090a77d919SArtem Dergachev if prev_st is not None: 8100a77d919SArtem Dergachev if s.store.is_different(prev_st): 8115740e77fSArtem Dergachev self._dump('</td></tr><tr><td align="left">') 8120a77d919SArtem Dergachev self.visit_store(st, prev_st) 8135740e77fSArtem Dergachev else: 814dd3c26a0STobias Hieta self._dump("<i> No changes!</i>") 8155740e77fSArtem Dergachev else: 8165740e77fSArtem Dergachev self._dump('</td></tr><tr><td align="left">') 8170a77d919SArtem Dergachev self.visit_store(st) 818dd3c26a0STobias Hieta self._dump("</td></tr>") 819beb85ad6SArtem Dergachev 820beb85ad6SArtem Dergachev def visit_generic_map(self, m, prev_m=None): 821beb85ad6SArtem Dergachev self._dump('<table border="0">') 822beb85ad6SArtem Dergachev 823beb85ad6SArtem Dergachev def dump_pair(m, k, is_added=None): 824dd3c26a0STobias Hieta self._dump( 825dd3c26a0STobias Hieta "<tr><td>%s</td>" 826beb85ad6SArtem Dergachev '<td align="left">%s</td>' 827beb85ad6SArtem Dergachev '<td align="left">%s</td></tr>' 828dd3c26a0STobias Hieta % (self._diff_plus_minus(is_added), k, m.generic_map[k]) 829dd3c26a0STobias Hieta ) 830beb85ad6SArtem Dergachev 831beb85ad6SArtem Dergachev if prev_m is not None: 832beb85ad6SArtem Dergachev removed, added = m.diff(prev_m) 833beb85ad6SArtem Dergachev for k in removed: 834beb85ad6SArtem Dergachev dump_pair(prev_m, k, False) 835beb85ad6SArtem Dergachev for k in added: 836beb85ad6SArtem Dergachev dump_pair(m, k, True) 837beb85ad6SArtem Dergachev else: 838beb85ad6SArtem Dergachev for k in m.generic_map: 839beb85ad6SArtem Dergachev dump_pair(m, k, None) 840beb85ad6SArtem Dergachev 841dd3c26a0STobias Hieta self._dump("</table>") 842beb85ad6SArtem Dergachev 84302f91ddfSArtem Dergachev def visit_generic_map_in_state(self, selector, title, s, prev_s=None): 844beb85ad6SArtem Dergachev m = getattr(s, selector) 84502f91ddfSArtem Dergachev prev_m = getattr(prev_s, selector) if prev_s is not None else None 84602f91ddfSArtem Dergachev if m is None and prev_m is None: 84702f91ddfSArtem Dergachev return 84802f91ddfSArtem Dergachev 849dd3c26a0STobias Hieta self._dump("<hr />") 850dd3c26a0STobias Hieta self._dump('<tr><td align="left">' "<b>%s: </b>" % title) 851beb85ad6SArtem Dergachev if m is None: 852dd3c26a0STobias Hieta self._dump("<i> Nothing!</i>") 853beb85ad6SArtem Dergachev else: 854beb85ad6SArtem Dergachev if prev_m is not None: 855beb85ad6SArtem Dergachev if m.is_different(prev_m): 856beb85ad6SArtem Dergachev self._dump('</td></tr><tr><td align="left">') 857beb85ad6SArtem Dergachev self.visit_generic_map(m, prev_m) 858beb85ad6SArtem Dergachev else: 859dd3c26a0STobias Hieta self._dump("<i> No changes!</i>") 860deb7accbSArtem Dergachev else: 861beb85ad6SArtem Dergachev self._dump('</td></tr><tr><td align="left">') 862beb85ad6SArtem Dergachev self.visit_generic_map(m) 863deb7accbSArtem Dergachev 864dd3c26a0STobias Hieta self._dump("</td></tr>") 865deb7accbSArtem Dergachev 866deb7accbSArtem Dergachev def visit_checker_messages(self, m, prev_m=None): 867deb7accbSArtem Dergachev self._dump('<table border="0">') 868deb7accbSArtem Dergachev 869deb7accbSArtem Dergachev def dump_line(l, is_added=None): 870dd3c26a0STobias Hieta self._dump( 871dd3c26a0STobias Hieta "<tr><td>%s</td>" 872dd3c26a0STobias Hieta '<td align="left">%s</td></tr>' % (self._diff_plus_minus(is_added), l) 873dd3c26a0STobias Hieta ) 874deb7accbSArtem Dergachev 875deb7accbSArtem Dergachev def dump_chk(chk, is_added=None): 876dd3c26a0STobias Hieta dump_line("<i>%s</i>:" % chk, is_added) 877deb7accbSArtem Dergachev 878deb7accbSArtem Dergachev if prev_m is not None: 879deb7accbSArtem Dergachev removed, added, updated = m.diff_messages(prev_m) 880deb7accbSArtem Dergachev for chk in removed: 881deb7accbSArtem Dergachev dump_chk(chk, False) 882deb7accbSArtem Dergachev for l in prev_m.items[chk].lines: 883deb7accbSArtem Dergachev dump_line(l, False) 884deb7accbSArtem Dergachev for chk in updated: 885deb7accbSArtem Dergachev dump_chk(chk) 886deb7accbSArtem Dergachev for l in m.items[chk].diff_lines(prev_m.items[chk]): 887dd3c26a0STobias Hieta dump_line(l[1:], l.startswith("+")) 888deb7accbSArtem Dergachev for chk in added: 889deb7accbSArtem Dergachev dump_chk(chk, True) 890deb7accbSArtem Dergachev for l in m.items[chk].lines: 891deb7accbSArtem Dergachev dump_line(l, True) 892deb7accbSArtem Dergachev else: 893deb7accbSArtem Dergachev for chk in m.items: 894deb7accbSArtem Dergachev dump_chk(chk) 895deb7accbSArtem Dergachev for l in m.items[chk].lines: 896deb7accbSArtem Dergachev dump_line(l) 897deb7accbSArtem Dergachev 898dd3c26a0STobias Hieta self._dump("</table>") 899deb7accbSArtem Dergachev 900deb7accbSArtem Dergachev def visit_checker_messages_in_state(self, s, prev_s=None): 901deb7accbSArtem Dergachev m = s.checker_messages 902deb7accbSArtem Dergachev prev_m = prev_s.checker_messages if prev_s is not None else None 903deb7accbSArtem Dergachev if m is None and prev_m is None: 904deb7accbSArtem Dergachev return 905deb7accbSArtem Dergachev 906dd3c26a0STobias Hieta self._dump("<hr />") 907dd3c26a0STobias Hieta self._dump('<tr><td align="left">' "<b>Checker State: </b>") 908deb7accbSArtem Dergachev if m is None: 909dd3c26a0STobias Hieta self._dump("<i> Nothing!</i>") 910deb7accbSArtem Dergachev else: 911deb7accbSArtem Dergachev if prev_m is not None: 912deb7accbSArtem Dergachev if m.is_different(prev_m): 913deb7accbSArtem Dergachev self._dump('</td></tr><tr><td align="left">') 914deb7accbSArtem Dergachev self.visit_checker_messages(m, prev_m) 915deb7accbSArtem Dergachev else: 916dd3c26a0STobias Hieta self._dump("<i> No changes!</i>") 917deb7accbSArtem Dergachev else: 918deb7accbSArtem Dergachev self._dump('</td></tr><tr><td align="left">') 919deb7accbSArtem Dergachev self.visit_checker_messages(m) 920deb7accbSArtem Dergachev 921dd3c26a0STobias Hieta self._dump("</td></tr>") 92216236077SArtem Dergachev 923b9c94f94SArtem Dergachev def visit_state(self, s, prev_s): 924b9c94f94SArtem Dergachev self.visit_store_in_state(s, prev_s) 925dd3c26a0STobias Hieta self.visit_environment_in_state("environment", "Expressions", s, prev_s) 926dd3c26a0STobias Hieta self.visit_generic_map_in_state("constraints", "Ranges", s, prev_s) 927dd3c26a0STobias Hieta self.visit_generic_map_in_state("dynamic_types", "Dynamic Types", s, prev_s) 928dd3c26a0STobias Hieta self.visit_environment_in_state( 929dd3c26a0STobias Hieta "constructing_objects", "Objects Under Construction", s, prev_s 930dd3c26a0STobias Hieta ) 931dd3c26a0STobias Hieta self.visit_environment_in_state( 932dd3c26a0STobias Hieta "index_of_element", "Indices Of Elements Under Construction", s, prev_s 933dd3c26a0STobias Hieta ) 934dd3c26a0STobias Hieta self.visit_environment_in_state( 935dd3c26a0STobias Hieta "pending_init_loops", "Pending Array Init Loop Expressions", s, prev_s 936dd3c26a0STobias Hieta ) 937dd3c26a0STobias Hieta self.visit_environment_in_state( 938dd3c26a0STobias Hieta "pending_destructors", "Indices of Elements Under Destruction", s, prev_s 939dd3c26a0STobias Hieta ) 940deb7accbSArtem Dergachev self.visit_checker_messages_in_state(s, prev_s) 94116236077SArtem Dergachev 94216236077SArtem Dergachev def visit_node(self, node): 943dd3c26a0STobias Hieta self._dump("%s [shape=record," % (node.node_name())) 944ad38e58eSArtem Dergachev if self._dark_mode: 945ad38e58eSArtem Dergachev self._dump('color="white",fontcolor="gray80",') 946ad38e58eSArtem Dergachev self._dump('label=<<table border="0">') 94716236077SArtem Dergachev 948dd3c26a0STobias Hieta self._dump( 949dd3c26a0STobias Hieta '<tr><td bgcolor="%s"><b>State %s</b></td></tr>' 950dd3c26a0STobias Hieta % ( 951dd3c26a0STobias Hieta "gray20" if self._dark_mode else "gray70", 952dd3c26a0STobias Hieta node.state.state_id if node.state is not None else "Unspecified", 953dd3c26a0STobias Hieta ) 954dd3c26a0STobias Hieta ) 955c6b5c5b9SArtem Dergachev if not self._topo_mode: 95616236077SArtem Dergachev self._dump('<tr><td align="left" width="0">') 95716236077SArtem Dergachev if len(node.points) > 1: 958dd3c26a0STobias Hieta self._dump("<b>Program points:</b></td></tr>") 95916236077SArtem Dergachev else: 960dd3c26a0STobias Hieta self._dump("<b>Program point:</b></td></tr>") 961dd3c26a0STobias Hieta self._dump( 962dd3c26a0STobias Hieta '<tr><td align="left" width="0">' 963dd3c26a0STobias Hieta '<table border="0" align="left" width="0">' 964dd3c26a0STobias Hieta ) 96516236077SArtem Dergachev for p in node.points: 96616236077SArtem Dergachev self.visit_program_point(p) 967dd3c26a0STobias Hieta self._dump("</table></td></tr>") 96816236077SArtem Dergachev 969c6b5c5b9SArtem Dergachev if node.state is not None and not self._topo_mode: 9705740e77fSArtem Dergachev prev_s = None 9715740e77fSArtem Dergachev # Do diffs only when we have a unique predecessor. 9725740e77fSArtem Dergachev # Don't do diffs on the leaf nodes because they're 9735740e77fSArtem Dergachev # the important ones. 974dd3c26a0STobias Hieta if ( 975dd3c26a0STobias Hieta self._do_diffs 976dd3c26a0STobias Hieta and len(node.predecessors) == 1 977dd3c26a0STobias Hieta and len(node.successors) > 0 978dd3c26a0STobias Hieta ): 9795740e77fSArtem Dergachev prev_s = self._graph.nodes[node.predecessors[0]].state 9805740e77fSArtem Dergachev self.visit_state(node.state, prev_s) 981dd3c26a0STobias Hieta self._dump_raw("</table>>];\n") 98216236077SArtem Dergachev 98316236077SArtem Dergachev def visit_edge(self, pred, succ): 984dd3c26a0STobias Hieta self._dump_raw( 985dd3c26a0STobias Hieta "%s -> %s%s;\n" 986dd3c26a0STobias Hieta % ( 987dd3c26a0STobias Hieta pred.node_name(), 988dd3c26a0STobias Hieta succ.node_name(), 989dd3c26a0STobias Hieta ' [color="white"]' if self._dark_mode else "", 990dd3c26a0STobias Hieta ) 991dd3c26a0STobias Hieta ) 99216236077SArtem Dergachev 99316236077SArtem Dergachev def visit_end_of_graph(self): 994dd3c26a0STobias Hieta self._dump_raw("}\n") 99516236077SArtem Dergachev 996e9e36354SArtem Dergachev if not self._dump_dot_only: 997e9e36354SArtem Dergachev import sys 998e9e36354SArtem Dergachev import tempfile 999e9e36354SArtem Dergachev 10005e876c54SBalazs Benics def write_temp_file(suffix, prefix, data): 1001dd3c26a0STobias Hieta fd, filename = tempfile.mkstemp(suffix, prefix, ".", True) 1002e9e36354SArtem Dergachev print('Writing "%s"...' % filename) 1003dd3c26a0STobias Hieta with os.fdopen(fd, "w") as fp: 1004e9e36354SArtem Dergachev fp.write(data) 1005dd3c26a0STobias Hieta print("Done! Please remember to remove the file.") 1006e9e36354SArtem Dergachev return filename 1007e9e36354SArtem Dergachev 1008e9e36354SArtem Dergachev try: 1009e9e36354SArtem Dergachev import graphviz 1010e9e36354SArtem Dergachev except ImportError: 1011e9e36354SArtem Dergachev # The fallback behavior if graphviz is not installed! 1012dd3c26a0STobias Hieta print("Python graphviz not found. Please invoke") 1013dd3c26a0STobias Hieta print(" $ pip install graphviz") 1014dd3c26a0STobias Hieta print("in order to enable automatic conversion to HTML.") 1015e9e36354SArtem Dergachev print() 1016dd3c26a0STobias Hieta print("You may also convert DOT to SVG manually via") 1017dd3c26a0STobias Hieta print(" $ dot -Tsvg input.dot -o output.svg") 1018e9e36354SArtem Dergachev print() 1019dd3c26a0STobias Hieta write_temp_file(".dot", "egraph-", self.output()) 1020e9e36354SArtem Dergachev return 1021e9e36354SArtem Dergachev 1022dd3c26a0STobias Hieta svg = graphviz.pipe("dot", "svg", self.output().encode()).decode() 1023e9e36354SArtem Dergachev 1024e9e36354SArtem Dergachev filename = write_temp_file( 1025dd3c26a0STobias Hieta ".html", 1026dd3c26a0STobias Hieta "egraph-", 1027dd3c26a0STobias Hieta '<html><body bgcolor="%s">%s</body></html>' 1028dd3c26a0STobias Hieta % ("#1a1a1a" if self._dark_mode else "white", svg), 1029dd3c26a0STobias Hieta ) 10300c3e24f7SElla Ma if self._dump_html_only: 10310c3e24f7SElla Ma return 1032dd3c26a0STobias Hieta if sys.platform == "win32": 1033e9e36354SArtem Dergachev os.startfile(filename) 1034dd3c26a0STobias Hieta elif sys.platform == "darwin": 1035e9e36354SArtem Dergachev os.system('open "%s"' % filename) 1036e9e36354SArtem Dergachev else: 1037e9e36354SArtem Dergachev os.system('xdg-open "%s"' % filename) 1038e9e36354SArtem Dergachev 103916236077SArtem Dergachev 10405fcf92e1SArtem Dergachev# ===-----------------------------------------------------------------------===# 10415fcf92e1SArtem Dergachev# Explorers know how to traverse the ExplodedGraph in a certain order. 10425fcf92e1SArtem Dergachev# They would invoke a Visitor on every node or edge they encounter. 10435fcf92e1SArtem Dergachev# ===-----------------------------------------------------------------------===# 10445fcf92e1SArtem Dergachev 10455fcf92e1SArtem Dergachev 104678566e45SArtem Dergachev# BasicExplorer explores the whole graph in no particular order. 1047c98872e3SValeriy Savchenkoclass BasicExplorer: 104816236077SArtem Dergachev def explore(self, graph, visitor): 104916236077SArtem Dergachev visitor.visit_begin_graph(graph) 105016236077SArtem Dergachev for node in sorted(graph.nodes): 1051dd3c26a0STobias Hieta logging.debug("Visiting " + node) 105216236077SArtem Dergachev visitor.visit_node(graph.nodes[node]) 105316236077SArtem Dergachev for succ in sorted(graph.nodes[node].successors): 1054dd3c26a0STobias Hieta logging.debug("Visiting edge: %s -> %s " % (node, succ)) 105516236077SArtem Dergachev visitor.visit_edge(graph.nodes[node], graph.nodes[succ]) 105616236077SArtem Dergachev visitor.visit_end_of_graph() 105716236077SArtem Dergachev 105816236077SArtem Dergachev 10590b26891fSArtem Dergachev# ===-----------------------------------------------------------------------===# 10600b26891fSArtem Dergachev# Trimmers cut out parts of the ExplodedGraph so that to focus on other parts. 10610b26891fSArtem Dergachev# Trimmers can be combined together by applying them sequentially. 10620b26891fSArtem Dergachev# ===-----------------------------------------------------------------------===# 10630b26891fSArtem Dergachev 10640b26891fSArtem Dergachev 10650b26891fSArtem Dergachev# SinglePathTrimmer keeps only a single path - the leftmost path from the root. 10660b26891fSArtem Dergachev# Useful when the trimmed graph is still too large. 1067c98872e3SValeriy Savchenkoclass SinglePathTrimmer: 10680b26891fSArtem Dergachev def trim(self, graph): 10690b26891fSArtem Dergachev visited_nodes = set() 107078566e45SArtem Dergachev node_id = graph.root_id 107178566e45SArtem Dergachev while True: 10720b26891fSArtem Dergachev visited_nodes.add(node_id) 107378566e45SArtem Dergachev node = graph.nodes[node_id] 10740b26891fSArtem Dergachev if len(node.successors) > 0: 107578566e45SArtem Dergachev succ_id = node.successors[0] 107678566e45SArtem Dergachev succ = graph.nodes[succ_id] 10770b26891fSArtem Dergachev node.successors = [succ_id] 10780b26891fSArtem Dergachev succ.predecessors = [node_id] 10790b26891fSArtem Dergachev if succ_id in visited_nodes: 108078566e45SArtem Dergachev break 108178566e45SArtem Dergachev node_id = succ_id 10820b26891fSArtem Dergachev else: 10830b26891fSArtem Dergachev break 1084dd3c26a0STobias Hieta graph.nodes = {node_id: graph.nodes[node_id] for node_id in visited_nodes} 108578566e45SArtem Dergachev 108678566e45SArtem Dergachev 10879289681eSArtem Dergachev# TargetedTrimmer keeps paths that lead to specific nodes and discards all 10889289681eSArtem Dergachev# other paths. Useful when you cannot use -trim-egraph (e.g. when debugging 10899289681eSArtem Dergachev# a crash). 1090c98872e3SValeriy Savchenkoclass TargetedTrimmer: 10919289681eSArtem Dergachev def __init__(self, target_nodes): 10929289681eSArtem Dergachev self._target_nodes = target_nodes 10939289681eSArtem Dergachev 10949289681eSArtem Dergachev @staticmethod 10959289681eSArtem Dergachev def parse_target_node(node, graph): 1096dd3c26a0STobias Hieta if node.startswith("0x"): 1097dd3c26a0STobias Hieta ret = "Node" + node 10989289681eSArtem Dergachev assert ret in graph.nodes 10999289681eSArtem Dergachev return ret 11009289681eSArtem Dergachev else: 11019289681eSArtem Dergachev for other_id in graph.nodes: 11029289681eSArtem Dergachev other = graph.nodes[other_id] 11039289681eSArtem Dergachev if other.node_id == int(node): 11049289681eSArtem Dergachev return other_id 11059289681eSArtem Dergachev 11069289681eSArtem Dergachev @staticmethod 11079289681eSArtem Dergachev def parse_target_nodes(target_nodes, graph): 1108dd3c26a0STobias Hieta return [ 1109dd3c26a0STobias Hieta TargetedTrimmer.parse_target_node(node, graph) 1110dd3c26a0STobias Hieta for node in target_nodes.split(",") 1111dd3c26a0STobias Hieta ] 11129289681eSArtem Dergachev 11139289681eSArtem Dergachev def trim(self, graph): 11149289681eSArtem Dergachev queue = self._target_nodes 11159289681eSArtem Dergachev visited_nodes = set() 11169289681eSArtem Dergachev 11179289681eSArtem Dergachev while len(queue) > 0: 11189289681eSArtem Dergachev node_id = queue.pop() 11199289681eSArtem Dergachev visited_nodes.add(node_id) 11209289681eSArtem Dergachev node = graph.nodes[node_id] 11219289681eSArtem Dergachev for pred_id in node.predecessors: 11229289681eSArtem Dergachev if pred_id not in visited_nodes: 11239289681eSArtem Dergachev queue.append(pred_id) 1124dd3c26a0STobias Hieta graph.nodes = {node_id: graph.nodes[node_id] for node_id in visited_nodes} 11259289681eSArtem Dergachev for node_id in graph.nodes: 11269289681eSArtem Dergachev node = graph.nodes[node_id] 1127dd3c26a0STobias Hieta node.successors = [ 1128dd3c26a0STobias Hieta succ_id for succ_id in node.successors if succ_id in visited_nodes 1129dd3c26a0STobias Hieta ] 1130dd3c26a0STobias Hieta node.predecessors = [ 1131dd3c26a0STobias Hieta succ_id for succ_id in node.predecessors if succ_id in visited_nodes 1132dd3c26a0STobias Hieta ] 11339289681eSArtem Dergachev 11349289681eSArtem Dergachev 11355fcf92e1SArtem Dergachev# ===-----------------------------------------------------------------------===# 11365fcf92e1SArtem Dergachev# The entry point to the script. 11375fcf92e1SArtem Dergachev# ===-----------------------------------------------------------------------===# 11385fcf92e1SArtem Dergachev 11395fcf92e1SArtem Dergachev 114016236077SArtem Dergachevdef main(): 1141e9e36354SArtem Dergachev parser = argparse.ArgumentParser( 1142dd3c26a0STobias Hieta description="Display and manipulate Exploded Graph dumps." 1143dd3c26a0STobias Hieta ) 1144dd3c26a0STobias Hieta parser.add_argument( 1145dd3c26a0STobias Hieta "filename", type=str, help="the .dot file produced by the Static Analyzer" 1146dd3c26a0STobias Hieta ) 1147dd3c26a0STobias Hieta parser.add_argument( 1148dd3c26a0STobias Hieta "-v", 1149dd3c26a0STobias Hieta "--verbose", 1150dd3c26a0STobias Hieta action="store_const", 1151dd3c26a0STobias Hieta dest="loglevel", 1152dd3c26a0STobias Hieta const=logging.DEBUG, 115316236077SArtem Dergachev default=logging.WARNING, 1154dd3c26a0STobias Hieta help="enable info prints", 1155dd3c26a0STobias Hieta ) 1156dd3c26a0STobias Hieta parser.add_argument( 1157dd3c26a0STobias Hieta "-d", 1158dd3c26a0STobias Hieta "--diff", 1159dd3c26a0STobias Hieta action="store_const", 1160dd3c26a0STobias Hieta dest="diff", 1161dd3c26a0STobias Hieta const=True, 1162dd3c26a0STobias Hieta default=False, 1163dd3c26a0STobias Hieta help="display differences between states", 1164dd3c26a0STobias Hieta ) 1165dd3c26a0STobias Hieta parser.add_argument( 1166dd3c26a0STobias Hieta "-t", 1167dd3c26a0STobias Hieta "--topology", 1168dd3c26a0STobias Hieta action="store_const", 1169dd3c26a0STobias Hieta dest="topology", 1170dd3c26a0STobias Hieta const=True, 1171dd3c26a0STobias Hieta default=False, 1172dd3c26a0STobias Hieta help="only display program points, omit states", 1173dd3c26a0STobias Hieta ) 1174dd3c26a0STobias Hieta parser.add_argument( 1175dd3c26a0STobias Hieta "-s", 1176dd3c26a0STobias Hieta "--single-path", 1177dd3c26a0STobias Hieta action="store_const", 1178dd3c26a0STobias Hieta dest="single_path", 1179dd3c26a0STobias Hieta const=True, 1180dd3c26a0STobias Hieta default=False, 1181dd3c26a0STobias Hieta help="only display the leftmost path in the graph " 1182dd3c26a0STobias Hieta "(useful for trimmed graphs that still " 1183dd3c26a0STobias Hieta "branch too much)", 1184dd3c26a0STobias Hieta ) 1185dd3c26a0STobias Hieta parser.add_argument( 1186dd3c26a0STobias Hieta "--to", 1187dd3c26a0STobias Hieta type=str, 1188dd3c26a0STobias Hieta default=None, 1189dd3c26a0STobias Hieta help="only display execution paths from the root " 1190dd3c26a0STobias Hieta "to the given comma-separated list of nodes " 1191dd3c26a0STobias Hieta "identified by a pointer or a stable ID; " 1192dd3c26a0STobias Hieta "compatible with --single-path", 1193dd3c26a0STobias Hieta ) 1194dd3c26a0STobias Hieta parser.add_argument( 1195dd3c26a0STobias Hieta "--dark", 1196dd3c26a0STobias Hieta action="store_const", 1197dd3c26a0STobias Hieta dest="dark", 1198dd3c26a0STobias Hieta const=True, 1199dd3c26a0STobias Hieta default=False, 1200dd3c26a0STobias Hieta help="dark mode", 1201dd3c26a0STobias Hieta ) 1202dd3c26a0STobias Hieta parser.add_argument( 1203dd3c26a0STobias Hieta "--gray", 1204dd3c26a0STobias Hieta action="store_const", 1205dd3c26a0STobias Hieta dest="gray", 1206dd3c26a0STobias Hieta const=True, 1207dd3c26a0STobias Hieta default=False, 1208dd3c26a0STobias Hieta help="black-and-white mode", 1209dd3c26a0STobias Hieta ) 12100c3e24f7SElla Ma dump_conflict = parser.add_mutually_exclusive_group() 12110c3e24f7SElla Ma dump_conflict.add_argument( 12120c3e24f7SElla Ma "--dump-html-only", 12130c3e24f7SElla Ma action="store_const", 12140c3e24f7SElla Ma dest="dump_html_only", 12150c3e24f7SElla Ma const=True, 12160c3e24f7SElla Ma default=False, 12170c3e24f7SElla Ma help="dump the rewritten egraph to a temporary HTML file, " 12180c3e24f7SElla Ma "but do not open it immediately as by default", 12190c3e24f7SElla Ma ) 12200c3e24f7SElla Ma dump_conflict.add_argument( 1221dd3c26a0STobias Hieta "--dump-dot-only", 1222dd3c26a0STobias Hieta action="store_const", 1223dd3c26a0STobias Hieta dest="dump_dot_only", 1224dd3c26a0STobias Hieta const=True, 1225dd3c26a0STobias Hieta default=False, 1226dd3c26a0STobias Hieta help="instead of writing an HTML file and immediately " 1227dd3c26a0STobias Hieta "displaying it, dump the rewritten dot file " 1228dd3c26a0STobias Hieta "to stdout", 1229dd3c26a0STobias Hieta ) 123016236077SArtem Dergachev args = parser.parse_args() 123116236077SArtem Dergachev logging.basicConfig(level=args.loglevel) 123216236077SArtem Dergachev 123316236077SArtem Dergachev graph = ExplodedGraph() 123416236077SArtem Dergachev with open(args.filename) as fd: 123516236077SArtem Dergachev for raw_line in fd: 123616236077SArtem Dergachev raw_line = raw_line.strip() 123716236077SArtem Dergachev graph.add_raw_line(raw_line) 123816236077SArtem Dergachev 12390b26891fSArtem Dergachev trimmers = [] 12409289681eSArtem Dergachev if args.to is not None: 1241dd3c26a0STobias Hieta trimmers.append( 1242dd3c26a0STobias Hieta TargetedTrimmer(TargetedTrimmer.parse_target_nodes(args.to, graph)) 1243dd3c26a0STobias Hieta ) 12440b26891fSArtem Dergachev if args.single_path: 12450b26891fSArtem Dergachev trimmers.append(SinglePathTrimmer()) 12460b26891fSArtem Dergachev 12470b26891fSArtem Dergachev explorer = BasicExplorer() 12480b26891fSArtem Dergachev 1249dd3c26a0STobias Hieta visitor = DotDumpVisitor( 12500c3e24f7SElla Ma args.diff, 12510c3e24f7SElla Ma args.dark, 12520c3e24f7SElla Ma args.gray, 12530c3e24f7SElla Ma args.topology, 12540c3e24f7SElla Ma args.dump_html_only, 12550c3e24f7SElla Ma args.dump_dot_only, 1256dd3c26a0STobias Hieta ) 125778566e45SArtem Dergachev 12580b26891fSArtem Dergachev for trimmer in trimmers: 12590b26891fSArtem Dergachev trimmer.trim(graph) 12600b26891fSArtem Dergachev 126116236077SArtem Dergachev explorer.explore(graph, visitor) 126216236077SArtem Dergachev 126316236077SArtem Dergachev 1264dd3c26a0STobias Hietaif __name__ == "__main__": 126516236077SArtem Dergachev main() 1266