Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(724)

Side by Side Diff: build/android/pylib/symbols/elf_symbolizer.py

Issue 311443002: elf_symbolizer: Use a process for max 4000 lookups and then restart (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2014 The Chromium Authors. All rights reserved. 1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import collections 5 import collections
6 import datetime 6 import datetime
7 import logging 7 import logging
8 import multiprocessing 8 import multiprocessing
9 import os 9 import os
10 import posixpath 10 import posixpath
(...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after
177 self._lib_file_name = posixpath.basename(symbolizer.elf_file_path) 177 self._lib_file_name = posixpath.basename(symbolizer.elf_file_path)
178 178
179 # The request queue (i.e. addresses pushed to addr2line's stdin and not 179 # The request queue (i.e. addresses pushed to addr2line's stdin and not
180 # yet retrieved on stdout) 180 # yet retrieved on stdout)
181 self._request_queue = collections.deque() 181 self._request_queue = collections.deque()
182 182
183 # This is essentially len(self._request_queue). It has been optimized to a 183 # This is essentially len(self._request_queue). It has been optimized to a
184 # separate field because turned out to be a perf hot-spot. 184 # separate field because turned out to be a perf hot-spot.
185 self.queue_size = 0 185 self.queue_size = 0
186 186
187 # Keep track of requests count so that we can replace addr2line
188 # processes before they consume all the memory of the machine
189 # and become slow and evil.
190 self._max_requests_per_process = 1000
Andrew Hayden (chromium.org) 2014/05/30 15:42:29 I think I left out a zero. I want to say this was
Primiano Tucci (use gerrit) 2014/05/30 16:56:20 Two notes: You eventually want to make this an arg
Daniel Bratell 2014/06/04 15:31:16 I did some measurements at my computer. It becomes
Daniel Bratell 2014/06/04 15:31:16 You were right, 1000 was too small. See measuremen
191 self._queued_request_count = 0
Primiano Tucci (use gerrit) 2014/05/30 16:56:20 You just need one "counter" variable, not two (see
192 self._received_answer_count = 0
193
187 # Objects required to handle the addr2line subprocess. 194 # Objects required to handle the addr2line subprocess.
188 self._proc = None # Subprocess.Popen(...) instance. 195 self._proc = None # Subprocess.Popen(...) instance.
189 self._thread = None # Threading.thread instance. 196 self._thread = None # Threading.thread instance.
190 self._out_queue = None # Queue.Queue instance (for buffering a2l stdout). 197 self._out_queue = None # Queue.Queue instance (for buffering a2l stdout).
191 self._RestartAddr2LineProcess() 198 self._RestartAddr2LineProcess()
192 199
193 def EnqueueRequest(self, addr, callback_arg): 200 def EnqueueRequest(self, addr, callback_arg):
194 """Pushes an address to addr2line's stdin (and keeps track of it).""" 201 """Pushes an address to addr2line's stdin (and keeps track of it)."""
195 self._symbolizer.requests_counter += 1 # For global "age" of requests. 202 self._symbolizer.requests_counter += 1 # For global "age" of requests.
196 req_idx = self._symbolizer.requests_counter 203 req_idx = self._symbolizer.requests_counter
197 self._request_queue.append((addr, callback_arg, req_idx)) 204 self._request_queue.append((addr, callback_arg, req_idx))
198 self.queue_size += 1 205 self.queue_size += 1
199 self._WriteToA2lStdin(addr) 206 self._queued_request_count += 1
Primiano Tucci (use gerrit) 2014/05/30 16:56:20 This extra logic here is unnecessary. You can jus
207 if self._queued_request_count <= self._max_requests_per_process:
208 # Otherwise they will be written once we restart the process.
209 self._WriteToA2lStdin(addr)
200 210
201 def WaitForIdle(self): 211 def WaitForIdle(self):
202 """Waits until all the pending requests have been symbolized.""" 212 """Waits until all the pending requests have been symbolized."""
203 while self.queue_size > 0: 213 while self.queue_size > 0:
204 self.WaitForNextSymbolInQueue() 214 self.WaitForNextSymbolInQueue()
205 215
206 def WaitForNextSymbolInQueue(self): 216 def WaitForNextSymbolInQueue(self):
207 """Waits for the next pending request to be symbolized.""" 217 """Waits for the next pending request to be symbolized."""
208 if not self.queue_size: 218 if not self.queue_size:
209 return 219 return
210 220
211 # This outer loop guards against a2l hanging (detecting stdout timeout). 221 # This outer loop guards against a2l hanging (detecting stdout timeout).
212 while True: 222 while True:
213 start_time = datetime.datetime.now() 223 start_time = datetime.datetime.now()
214 timeout = datetime.timedelta(seconds=self._symbolizer.addr2line_timeout) 224 timeout = datetime.timedelta(seconds=self._symbolizer.addr2line_timeout)
215 225
216 # The inner loop guards against a2l crashing (checking if it exited). 226 # The inner loop guards against a2l crashing (checking if it exited).
217 while (datetime.datetime.now() - start_time < timeout): 227 while (datetime.datetime.now() - start_time < timeout):
228 # Restart the addr2line process if it has been running for a
229 # while. It keeps growing so otherwise it will make the
230 # whole computer unresponsive, including itself.
231 if self._received_answer_count >= self._max_requests_per_process:
232 self._queued_request_count -= self._received_answer_count
Primiano Tucci (use gerrit) 2014/05/30 16:56:20 This math seems rendudant. Here you just want to k
Daniel Bratell 2014/06/04 15:31:16 Seems it became a little bit faster by this change
233 self._received_answer_count = 0
234 self._RestartAddr2LineProcess()
218 # poll() returns !None if the process exited. a2l should never exit. 235 # poll() returns !None if the process exited. a2l should never exit.
219 if self._proc.poll(): 236 if self._proc.poll():
220 logging.warning('addr2line crashed, respawning (lib: %s).' % 237 logging.warning('addr2line crashed, respawning (lib: %s).' %
221 self._lib_file_name) 238 self._lib_file_name)
222 self._RestartAddr2LineProcess() 239 self._RestartAddr2LineProcess()
223 # TODO(primiano): the best thing to do in this case would be 240 # TODO(primiano): the best thing to do in this case would be
224 # shrinking the pool size as, very likely, addr2line is crashed 241 # shrinking the pool size as, very likely, addr2line is crashed
225 # due to low memory (and the respawned one will die again soon). 242 # due to low memory (and the respawned one will die again soon).
226 243
227 try: 244 try:
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
290 source_line = int(m.group(2)) 307 source_line = int(m.group(2))
291 else: 308 else:
292 logging.warning('Got invalid symbol path from addr2line: %s' % line2) 309 logging.warning('Got invalid symbol path from addr2line: %s' % line2)
293 310
294 sym_info = ELFSymbolInfo(name, source_path, source_line) 311 sym_info = ELFSymbolInfo(name, source_path, source_line)
295 if prev_sym_info: 312 if prev_sym_info:
296 prev_sym_info.inlined_by = sym_info 313 prev_sym_info.inlined_by = sym_info
297 if not innermost_sym_info: 314 if not innermost_sym_info:
298 innermost_sym_info = sym_info 315 innermost_sym_info = sym_info
299 316
317 self._received_answer_count += 1
300 self._symbolizer.callback(innermost_sym_info, callback_arg) 318 self._symbolizer.callback(innermost_sym_info, callback_arg)
301 319
302 def _RestartAddr2LineProcess(self): 320 def _RestartAddr2LineProcess(self):
303 if self._proc: 321 if self._proc:
304 self.Terminate() 322 self.Terminate()
305 323
306 # The only reason of existence of this Queue (and the corresponding 324 # The only reason of existence of this Queue (and the corresponding
307 # Thread below) is the lack of a subprocess.stdout.poll_avail_lines(). 325 # Thread below) is the lack of a subprocess.stdout.poll_avail_lines().
308 # Essentially this is a pipe able to extract a couple of lines atomically. 326 # Essentially this is a pipe able to extract a couple of lines atomically.
309 self._out_queue = Queue.Queue() 327 self._out_queue = Queue.Queue()
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
374 self.name = name 392 self.name = name
375 self.source_path = source_path 393 self.source_path = source_path
376 self.source_line = source_line 394 self.source_line = source_line
377 # In the case of |inlines|=True, the |inlined_by| points to the outer 395 # In the case of |inlines|=True, the |inlined_by| points to the outer
378 # function inlining the current one (and so on, to form a chain). 396 # function inlining the current one (and so on, to form a chain).
379 self.inlined_by = None 397 self.inlined_by = None
380 398
381 def __str__(self): 399 def __str__(self):
382 return '%s [%s:%d]' % ( 400 return '%s [%s:%d]' % (
383 self.name or '??', self.source_path or '??', self.source_line or 0) 401 self.name or '??', self.source_path or '??', self.source_line or 0)
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698