Index: build/android/pylib/symbols/elf_symbolizer.py |
diff --git a/build/android/pylib/symbols/elf_symbolizer.py b/build/android/pylib/symbols/elf_symbolizer.py |
index 90e99e5a2757770b30d8d3a89d0a34b589212780..b294654f20b1ee3c7f0cdb5c142789f3bc1b5933 100644 |
--- a/build/android/pylib/symbols/elf_symbolizer.py |
+++ b/build/android/pylib/symbols/elf_symbolizer.py |
@@ -15,6 +15,13 @@ import sys |
import threading |
+# addr2line builds a possibly infinite memory cache that can exhaust |
+# the computer's memory if allowed to grow for too long. This constant |
+# controls how many lookups we do before restarting the process. 4000 |
+# gives near peak performance without extreme memory usage. |
+ADDR2LINE_RECYCLE_LIMIT = 4000 |
+ |
+ |
class ELFSymbolizer(object): |
"""An uber-fast (multiprocessing, pipelined and asynchronous) ELF symbolizer. |
@@ -117,6 +124,7 @@ class ELFSymbolizer(object): |
# Essentially, this drains all the addr2line(s) out queues. |
for a2l_to_purge in self._a2l_instances: |
a2l_to_purge.ProcessAllResolvedSymbolsInQueue() |
+ a2l_to_purge.RecycleIfNecessary() |
# Find the best instance according to this logic: |
# 1. Find an existing instance with the shortest queue. |
@@ -184,6 +192,10 @@ class ELFSymbolizer(object): |
# separate field because turned out to be a perf hot-spot. |
self.queue_size = 0 |
+ # Keep track of the number of symbols a process has processed to |
+ # avoid a single process growing too big and using all the memory. |
+ self._processed_symbols_count = 0 |
+ |
# Objects required to handle the addr2line subprocess. |
self._proc = None # Subprocess.Popen(...) instance. |
self._thread = None # Threading.thread instance. |
@@ -251,6 +263,15 @@ class ELFSymbolizer(object): |
break |
self._ProcessSymbolOutput(lines) |
+ def RecycleIfNecessary(self): |
+ """Restarts the process if it has been used for too long. |
+ |
+ A long running addr2line process will consume excessive amounts |
+ of memory without any gain in performance.""" |
+ if self._processed_symbols_count >= ADDR2LINE_RECYCLE_LIMIT: |
+ self._RestartAddr2LineProcess() |
+ |
+ |
def Terminate(self): |
"""Kills the underlying addr2line process. |
@@ -297,6 +318,7 @@ class ELFSymbolizer(object): |
if not innermost_sym_info: |
innermost_sym_info = sym_info |
+ self._processed_symbols_count += 1 |
self._symbolizer.callback(innermost_sym_info, callback_arg) |
def _RestartAddr2LineProcess(self): |
@@ -325,6 +347,8 @@ class ELFSymbolizer(object): |
self._thread.daemon = True # Don't prevent early process exit. |
self._thread.start() |
+ self._processed_symbols_count = 0 |
+ |
# Replay the pending requests on the new process (only for the case |
# of a hung addr2line timing out during the game). |
for (addr, _, _) in self._request_queue: |