def __init__(self): self.coverage_collector = CoverageCollector() self.call_graph_collector = CallStackCollector() # Cache the filename that should trace self.should_trace_cache = set() # Cache the filename that should not trace self.should_not_trace_cache = set() self.do_call_graph = False self.pylib_match = get_pylib_matcher()
class CollectorManager: def __init__(self): self.coverage_collector = CoverageCollector() self.call_graph_collector = CallStackCollector() # Cache the filename that should trace self.should_trace_cache = set() # Cache the filename that should not trace self.should_not_trace_cache = set() self.do_call_graph = False self.pylib_match = get_pylib_matcher() def harvest_coverage_data(self): cov_data = self.coverage_collector.harvest_data() return cov_data def harvest_call_graph_data(self): graph_data = self.call_graph_collector.harvest_data() return graph_data def harvest_graph(self): binary = self.call_graph_collector.draw_graph() return binary def clear_graph(self): self.call_graph_collector.reset() def trace(self, frame, event, arg_unused): """ The main trace function, decide what we need to do for each function call or line execution. :param frame: the current stack frame :param event: event is a string: 'call', 'line', 'return', 'exception', 'c_call', 'c_return', or 'c_exception'. :param arg_unused: we don't need this, however settrace need it. :return: return a reference to a local trace function to be used that scope, which means we need to return the trace function itself. """ # Be careful that the filename can be a relative path name, such as ../logic/file1.py cur_file_path = frame.f_code.co_filename line_no = frame.f_lineno if cur_file_path in self.should_not_trace_cache: return self.trace real_file_path = get_real_path(cur_file_path) if cur_file_path in self.should_trace_cache: self.coverage_collector.collect(real_file_path, line_no) if self.do_call_graph: self.call_graph_collector.collect(frame, event) else: if not cur_file_path: print "-" * 20, "empty file name, ignore trace ", cur_file_path self.should_not_trace_cache.add(cur_file_path) return self.trace elif cur_file_path.startswith('<'): # something like <string>, <module> print "-" * 20, "invalid filename, ignore trace ", cur_file_path self.should_not_trace_cache.add(cur_file_path) return self.trace elif not cur_file_path.endswith( ".py") and not cur_file_path.endswith( ".pyc") and not cur_file_path.endswith("$py.class"): print "-" * 20, "not a python source file, ignore trace ", cur_file_path self.should_not_trace_cache.add(cur_file_path) return self.trace elif not os.path.exists(real_file_path): # sometimes we only have .pyc file and doesn't have .py source file. print "-" * 20, "source file doesn't exists, ignore trace ", real_file_path self.should_not_trace_cache.add(cur_file_path) return self.trace # then check if this is lib file elif self.pylib_match and self.pylib_match.match(real_file_path): print "-" * 20, "library file, ignore trace ", real_file_path self.should_not_trace_cache.add(cur_file_path) return self.trace else: print "+" * 20, "trace this file ", real_file_path self.should_trace_cache.add(cur_file_path) self.coverage_collector.collect(real_file_path, line_no) if self.do_call_graph: self.call_graph_collector.collect(frame, event) return self.trace def start_trace(self): self.do_call_graph = False sys.settrace(self.trace) threading.settrace(self.trace)
class CollectorManager: def __init__(self): self.coverage_collector = CoverageCollector() self.call_graph_collector = CallStackCollector() # Cache the filename that should trace self.should_trace_cache = set() # Cache the filename that should not trace self.should_not_trace_cache = set() self.do_call_graph = False self.pylib_match = get_pylib_matcher() def harvest_coverage_data(self): cov_data = self.coverage_collector.harvest_data() return cov_data def harvest_call_graph_data(self): graph_data = self.call_graph_collector.harvest_data() return graph_data def harvest_graph(self): binary = self.call_graph_collector.draw_graph() return binary def clear_graph(self): self.call_graph_collector.reset() def trace(self, frame, event, arg_unused): """ The main trace function, decide what we need to do for each function call or line execution. :param frame: the current stack frame :param event: event is a string: 'call', 'line', 'return', 'exception', 'c_call', 'c_return', or 'c_exception'. :param arg_unused: we don't need this, however settrace need it. :return: return a reference to a local trace function to be used that scope, which means we need to return the trace function itself. """ # Be careful that the filename can be a relative path name, such as ../logic/file1.py cur_file_path = frame.f_code.co_filename line_no = frame.f_lineno if cur_file_path in self.should_not_trace_cache: return self.trace real_file_path = get_real_path(cur_file_path) if cur_file_path in self.should_trace_cache: self.coverage_collector.collect(real_file_path, line_no) if self.do_call_graph: self.call_graph_collector.collect(frame, event) else: if not cur_file_path: print "-" * 20, "empty file name, ignore trace ", cur_file_path self.should_not_trace_cache.add(cur_file_path) return self.trace elif cur_file_path.startswith('<'): # something like <string>, <module> print "-" * 20, "invalid filename, ignore trace ", cur_file_path self.should_not_trace_cache.add(cur_file_path) return self.trace elif not cur_file_path.endswith(".py") and not cur_file_path.endswith( ".pyc") and not cur_file_path.endswith( "$py.class"): print "-" * 20, "not a python source file, ignore trace ", cur_file_path self.should_not_trace_cache.add(cur_file_path) return self.trace elif not os.path.exists(real_file_path): # sometimes we only have .pyc file and doesn't have .py source file. print "-" * 20, "source file doesn't exists, ignore trace ", real_file_path self.should_not_trace_cache.add(cur_file_path) return self.trace # then check if this is lib file elif self.pylib_match and self.pylib_match.match(real_file_path): print "-" * 20, "library file, ignore trace ", real_file_path self.should_not_trace_cache.add(cur_file_path) return self.trace else: print "+" * 20, "trace this file ", real_file_path self.should_trace_cache.add(cur_file_path) self.coverage_collector.collect(real_file_path, line_no) if self.do_call_graph: self.call_graph_collector.collect(frame, event) return self.trace def start_trace(self): self.do_call_graph = False sys.settrace(self.trace) threading.settrace(self.trace)