 Chromium Code Reviews
 Chromium Code Reviews 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
    
  
    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| OLD | NEW | 
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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) | 
| OLD | NEW |