def TracerMain_(log, storage_path, backend_name, device_id, pid, interval, count, trace_native_heap): """Entry point for the background periodic tracer task.""" # Initialize storage. storage = file_storage.Storage(storage_path) # Initialize the backend. backend = backends.GetBackend(backend_name) for k, v in storage.LoadSettings(backend_name).iteritems(): backend.settings[k] = v # Initialize the device. device = backends.GetDevice(backend_name, device_id) for k, v in storage.LoadSettings(device_id).iteritems(): device.settings[k] = v # Start periodic tracing. process = device.GetProcess(pid) log.put((1, 'Starting trace (%d dumps x %s s.). Device: %s, process: %s' % ( count, interval, device.name, process.name))) datetime_str = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M') archive_name = '%s - %s - %s' % (datetime_str, device.name, process.name) archive = storage.OpenArchive(archive_name, create=True) heaps_to_symbolize = [] for i in xrange(1, count + 1): # [1, count] range is easier to handle. process = device.GetProcess(pid) if not process: log.put((100, 'Process %d died.' % pid)) return 1 # Calculate the completion rate proportionally to 80%. We keep the remaining # 20% for the final symbolization step (just an approximate estimation). completion = 80 * i / count log.put((completion, 'Dumping trace %d of %d' % (i, count))) archive.StartNewSnapshot() mmaps = process.DumpMemoryMaps() log.put((completion, 'Dumped %d memory maps' % len(mmaps))) archive.StoreMemMaps(mmaps) if trace_native_heap: nheap = process.DumpNativeHeap() log.put((completion, 'Dumped %d native allocs' % len(nheap.allocations))) archive.StoreNativeHeap(nheap) heaps_to_symbolize += [nheap] if i < count: time.sleep(interval) log.put((90, 'Symbolizing')) symbols = backend.ExtractSymbols(heaps_to_symbolize, device.settings['native_symbol_paths'] or '') expected_symbols_count = len(set.union( *[set(x.stack_frames.iterkeys()) for x in heaps_to_symbolize])) log.put((99, 'Symbolization complete. Got %d symbols (%.1f%%).' % ( len(symbols), 100.0 * len(symbols) / expected_symbols_count))) archive.StoreSymbols(symbols) log.put((100, 'Trace complete.')) return 0
_HTTP_OK = '200 OK' _HTTP_GONE = '410 Gone' _HTTP_NOT_FOUND = '404 Not Found' _HTTP_INTERNAL_ERROR = '500 Internal Server Error' _PERSISTENT_STORAGE_PATH = os.path.join(os.path.expanduser('~'), '.config', 'memory_inspector') _CONTENT_DIR = os.path.abspath( os.path.join(os.path.dirname(__file__), 'www_content')) _APP_PROCESS_RE = r'^[\w.:]+$' # Regex for matching app processes. _STATS_HIST_SIZE = 120 # Keep at most 120 samples of stats per process. _CACHE_LEN = 10 # Max length of |_cached_objs|. # |_cached_objs| keeps the state of short-lived objects that the client needs to # _cached_objs subsequent AJAX calls. _cached_objs = collections.OrderedDict() _persistent_storage = file_storage.Storage(_PERSISTENT_STORAGE_PATH) _proc_stats_history = { } # /Android/device/PID -> deque([stats@T=0, stats@T=1]) class UriHandler(object): """Base decorator used to automatically route /requests/by/path. Each handler is called with the following args: args: a tuple of the matching regex groups. req_vars: a dictionary of request args (querystring for GET, body for POST). Each handler must return a tuple with the following elements: http_code: a string with the HTTP status code (e.g., '200 - OK') headers: a list of HTTP headers (e.g., [('Content-Type': 'foo/bar')]) body: the HTTP response body. """
def setUp(self): self._storage_path = tempfile.mkdtemp() self._storage = file_storage.Storage(self._storage_path)