Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Public Member Functions | Static Public Member Functions | Public Attributes | Static Public Attributes | Protected Member Functions | Protected Attributes | List of all members
elf_symbolizer.ELFSymbolizer.Addr2Line Class Reference
Inheritance diagram for elf_symbolizer.ELFSymbolizer.Addr2Line:

Public Member Functions

 __init__ (self, symbolizer)
 
 EnqueueRequest (self, addr, callback_arg)
 
 WaitForIdle (self)
 
 WaitForNextSymbolInQueue (self)
 
 ProcessAllResolvedSymbolsInQueue (self)
 
 RecycleIfNecessary (self)
 
 Terminate (self)
 
 first_request_id (self)
 

Static Public Member Functions

 StdoutReaderThread (process_pipe, queue, inlines)
 

Public Attributes

 queue_size
 

Static Public Attributes

 SYM_ADDR_RE = re.compile(r'([^:]+):(\?|\d+).*')
 

Protected Member Functions

 _WriteToA2lStdin (self, addr)
 
 _ProcessSymbolOutput (self, lines)
 
 _RestartAddr2LineProcess (self)
 

Protected Attributes

 _symbolizer
 
 _lib_file_name
 
 _request_queue
 
 _processed_symbols_count
 
 _proc
 
 _thread
 
 _out_queue
 

Detailed Description

A python wrapper around an addr2line instance.

The communication with the addr2line process looks as follows:
[STDIN]         [STDOUT]  (from addr2line's viewpoint)
> f001111
> f002222
            < Symbol::Name(foo, bar) for f001111
            < /path/to/source/file.c:line_number
> f003333
            < Symbol::Name2() for f002222
            < /path/to/source/file.c:line_number
            < Symbol::Name3() for f003333
            < /path/to/source/file.c:line_number

Definition at line 207 of file elf_symbolizer.py.

Constructor & Destructor Documentation

◆ __init__()

elf_symbolizer.ELFSymbolizer.Addr2Line.__init__ (   self,
  symbolizer 
)

Definition at line 225 of file elf_symbolizer.py.

225 def __init__(self, symbolizer):
226 self._symbolizer = symbolizer
227 self._lib_file_name = posixpath.basename(symbolizer.elf_file_path)
228
229 # The request queue (i.e. addresses pushed to addr2line's stdin and not
230 # yet retrieved on stdout)
231 self._request_queue = collections.deque()
232
233 # This is essentially len(self._request_queue). It has been optimized to a
234 # separate field because turned out to be a perf hot-spot.
235 self.queue_size = 0
236
237 # Keep track of the number of symbols a process has processed to
238 # avoid a single process growing too big and using all the memory.
239 self._processed_symbols_count = 0
240
241 # Objects required to handle the addr2line subprocess.
242 self._proc = None # Subprocess.Popen(...) instance.
243 self._thread = None # Threading.thread instance.
244 self._out_queue = None # queue.Queue instance (for buffering a2l stdout).
245 self._RestartAddr2LineProcess()
246

Member Function Documentation

◆ _ProcessSymbolOutput()

elf_symbolizer.ELFSymbolizer.Addr2Line._ProcessSymbolOutput (   self,
  lines 
)
protected
Parses an addr2line symbol output and triggers the client callback.

Definition at line 338 of file elf_symbolizer.py.

338 def _ProcessSymbolOutput(self, lines):
339 """Parses an addr2line symbol output and triggers the client callback."""
340 (_, callback_arg, _) = self._request_queue.popleft()
341 self.queue_size -= 1
342
343 innermost_sym_info = None
344 sym_info = None
345 for (line1, line2) in lines:
346 prev_sym_info = sym_info
347 name = line1 if not line1.startswith('?') else None
348 source_path = None
349 source_line = None
350 m = ELFSymbolizer.Addr2Line.SYM_ADDR_RE.match(line2)
351 if m:
352 if not m.group(1).startswith('?'):
353 source_path = m.group(1)
354 if not m.group(2).startswith('?'):
355 source_line = int(m.group(2))
356 else:
357 logging.warning(
358 'Got invalid symbol path from addr2line: %s' % line2)
359
360 # In case disambiguation is on, and needed
361 was_ambiguous = False
362 disambiguated = False
363 if self._symbolizer.disambiguate:
364 if source_path and not posixpath.isabs(source_path):
365 path = self._symbolizer.disambiguation_table.get(
366 source_path)
367 was_ambiguous = True
368 disambiguated = path is not None
369 source_path = path if disambiguated else source_path
370
371 # Use absolute paths (so that paths are consistent, as disambiguation
372 # uses absolute paths)
373 if source_path and not was_ambiguous:
374 source_path = os.path.abspath(source_path)
375
376 if source_path and self._symbolizer.strip_base_path:
377 # Strip the base path
378 source_path = re.sub(
379 '^' + self._symbolizer.strip_base_path,
380 self._symbolizer.source_root_path or '', source_path)
381
382 sym_info = ELFSymbolInfo(name, source_path, source_line,
383 was_ambiguous, disambiguated)
384 if prev_sym_info:
385 prev_sym_info.inlined_by = sym_info
386 if not innermost_sym_info:
387 innermost_sym_info = sym_info
388
389 self._processed_symbols_count += 1
390 self._symbolizer.callback(innermost_sym_info, callback_arg)
391
Type::kYUV Type::kRGBA() int(0.7 *637)
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback

◆ _RestartAddr2LineProcess()

elf_symbolizer.ELFSymbolizer.Addr2Line._RestartAddr2LineProcess (   self)
protected

Definition at line 392 of file elf_symbolizer.py.

392 def _RestartAddr2LineProcess(self):
393 if self._proc:
394 self.Terminate()
395
396 # The only reason of existence of this Queue (and the corresponding
397 # Thread below) is the lack of a subprocess.stdout.poll_avail_lines().
398 # Essentially this is a pipe able to extract a couple of lines atomically.
399 self._out_queue = queue.Queue()
400
401 # Start the underlying addr2line process in line buffered mode.
402
403 cmd = [
404 self._symbolizer.addr2line_path, '--functions', '--demangle',
405 '--exe=' + self._symbolizer.elf_file_path
406 ]
407 if self._symbolizer.inlines:
408 cmd += ['--inlines']
409 self._proc = subprocess.Popen(
410 cmd,
411 stdout=subprocess.PIPE,
412 stdin=subprocess.PIPE,
413 stderr=sys.stderr,
414 close_fds=True)
415
416 # Start the poller thread, which simply moves atomically the lines read
417 # from the addr2line's stdout to the |_out_queue|.
418 self._thread = threading.Thread(
419 target=ELFSymbolizer.Addr2Line.StdoutReaderThread,
420 args=(self._proc.stdout, self._out_queue,
421 self._symbolizer.inlines))
422 self._thread.daemon = True # Don't prevent early process exit.
423 self._thread.start()
424
425 self._processed_symbols_count = 0
426
427 # Replay the pending requests on the new process (only for the case
428 # of a hung addr2line timing out during the game).
429 for (addr, _, _) in self._request_queue:
430 self._WriteToA2lStdin(addr)
431

◆ _WriteToA2lStdin()

elf_symbolizer.ELFSymbolizer.Addr2Line._WriteToA2lStdin (   self,
  addr 
)
protected

Definition at line 330 of file elf_symbolizer.py.

330 def _WriteToA2lStdin(self, addr):
331 self._proc.stdin.write(('%s\n' % hex(addr)).encode())
332 if self._symbolizer.inlines:
333 # In the case of inlines we output an extra blank line, which causes
334 # addr2line to emit a (??,??:0) tuple that we use as a boundary marker.
335 self._proc.stdin.write('\n')
336 self._proc.stdin.flush()
337
static void encode(uint8_t output[16], const uint32_t input[4])
Definition SkMD5.cpp:240

◆ EnqueueRequest()

elf_symbolizer.ELFSymbolizer.Addr2Line.EnqueueRequest (   self,
  addr,
  callback_arg 
)
Pushes an address to addr2line's stdin (and keeps track of it).

Definition at line 247 of file elf_symbolizer.py.

247 def EnqueueRequest(self, addr, callback_arg):
248 """Pushes an address to addr2line's stdin (and keeps track of it)."""
249 self._symbolizer.requests_counter += 1 # For global "age" of requests.
250 req_idx = self._symbolizer.requests_counter
251 self._request_queue.append((addr, callback_arg, req_idx))
252 self.queue_size += 1
253 self._WriteToA2lStdin(addr)
254
static void append(char **dst, size_t *count, const char *src, size_t n)
Definition editor.cpp:211

◆ first_request_id()

elf_symbolizer.ELFSymbolizer.Addr2Line.first_request_id (   self)
Returns the request_id of the oldest pending request in the queue.

Definition at line 464 of file elf_symbolizer.py.

464 def first_request_id(self):
465 """Returns the request_id of the oldest pending request in the queue."""
466 return self._request_queue[0][2] if self._request_queue else 0
467
468

◆ ProcessAllResolvedSymbolsInQueue()

elf_symbolizer.ELFSymbolizer.Addr2Line.ProcessAllResolvedSymbolsInQueue (   self)
Consumes all the addr2line output lines produced (without blocking).

Definition at line 299 of file elf_symbolizer.py.

299 def ProcessAllResolvedSymbolsInQueue(self):
300 """Consumes all the addr2line output lines produced (without blocking)."""
301 if not self.queue_size:
302 return
303 while True:
304 try:
305 lines = self._out_queue.get_nowait()
306 except queue.Empty:
307 break
308 self._ProcessSymbolOutput(lines)
309

◆ RecycleIfNecessary()

elf_symbolizer.ELFSymbolizer.Addr2Line.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.

Definition at line 310 of file elf_symbolizer.py.

310 def RecycleIfNecessary(self):
311 """Restarts the process if it has been used for too long.
312
313 A long running addr2line process will consume excessive amounts
314 of memory without any gain in performance."""
315 if self._processed_symbols_count >= ADDR2LINE_RECYCLE_LIMIT:
316 self._RestartAddr2LineProcess()
317

◆ StdoutReaderThread()

elf_symbolizer.ELFSymbolizer.Addr2Line.StdoutReaderThread (   process_pipe,
  queue,
  inlines 
)
static
The poller thread fn, which moves the addr2line stdout to the |queue|.

This is the only piece of code not running on the main thread. It merely
writes to a Queue, which is thread-safe. In the case of inlines, it
detects the ??,??:0 marker and sends the lines atomically, such that the
main thread always receives all the lines corresponding to one symbol in
one shot.

Definition at line 433 of file elf_symbolizer.py.

433 def StdoutReaderThread(process_pipe, queue, inlines):
434 """The poller thread fn, which moves the addr2line stdout to the |queue|.
435
436 This is the only piece of code not running on the main thread. It merely
437 writes to a Queue, which is thread-safe. In the case of inlines, it
438 detects the ??,??:0 marker and sends the lines atomically, such that the
439 main thread always receives all the lines corresponding to one symbol in
440 one shot."""
441 try:
442 lines_for_one_symbol = []
443 while True:
444 line1 = process_pipe.readline().decode().rstrip('\r\n')
445 line2 = process_pipe.readline().decode().rstrip('\r\n')
446 if not line1 or not line2:
447 break
448 inline_has_more_lines = inlines and (
449 len(lines_for_one_symbol) == 0 or
450 (line1 != '??' and line2 != '??:0'))
451 if not inlines or inline_has_more_lines:
452 lines_for_one_symbol += [(line1, line2)]
453 if inline_has_more_lines:
454 continue
455 queue.put(lines_for_one_symbol)
456 lines_for_one_symbol = []
457 process_pipe.close()
458
459 # Every addr2line processes will die at some point, please die silently.
460 except (IOError, OSError):
461 pass
462
static DecodeResult decode(std::string path)

◆ Terminate()

elf_symbolizer.ELFSymbolizer.Addr2Line.Terminate (   self)
Kills the underlying addr2line process.

The poller |_thread| will terminate as well due to the broken pipe.

Definition at line 318 of file elf_symbolizer.py.

318 def Terminate(self):
319 """Kills the underlying addr2line process.
320
321 The poller |_thread| will terminate as well due to the broken pipe."""
322 try:
323 self._proc.kill()
324 self._proc.communicate(
325 ) # Essentially wait() without risking deadlock.
326 except Exception: # An exception while terminating? How interesting.
327 pass
328 self._proc = None
329

◆ WaitForIdle()

elf_symbolizer.ELFSymbolizer.Addr2Line.WaitForIdle (   self)
Waits until all the pending requests have been symbolized.

Definition at line 255 of file elf_symbolizer.py.

255 def WaitForIdle(self):
256 """Waits until all the pending requests have been symbolized."""
257 while self.queue_size > 0:
258 self.WaitForNextSymbolInQueue()
259

◆ WaitForNextSymbolInQueue()

elf_symbolizer.ELFSymbolizer.Addr2Line.WaitForNextSymbolInQueue (   self)
Waits for the next pending request to be symbolized.

Definition at line 260 of file elf_symbolizer.py.

260 def WaitForNextSymbolInQueue(self):
261 """Waits for the next pending request to be symbolized."""
262 if not self.queue_size:
263 return
264
265 # This outer loop guards against a2l hanging (detecting stdout timeout).
266 while True:
267 start_time = datetime.datetime.now()
268 timeout = datetime.timedelta(
269 seconds=self._symbolizer.addr2line_timeout)
270
271 # The inner loop guards against a2l crashing (checking if it exited).
272 while (datetime.datetime.now() - start_time < timeout):
273 # poll() returns !None if the process exited. a2l should never exit.
274 if self._proc.poll():
275 logging.warning(
276 'addr2line crashed, respawning (lib: %s).' %
277 self._lib_file_name)
278 self._RestartAddr2LineProcess()
279 # TODO(primiano): the best thing to do in this case would be
280 # shrinking the pool size as, very likely, addr2line is crashed
281 # due to low memory (and the respawned one will die again soon).
282
283 try:
284 lines = self._out_queue.get(block=True, timeout=0.25)
285 except queue.Empty:
286 # On timeout (1/4 s.) repeat the inner loop and check if either the
287 # addr2line process did crash or we waited its output for too long.
288 continue
289
290 # In nominal conditions, we get straight to this point.
291 self._ProcessSymbolOutput(lines)
292 return
293
294 # If this point is reached, we waited more than |addr2line_timeout|.
295 logging.warning('Hung addr2line process, respawning (lib: %s).'
296 % self._lib_file_name)
297 self._RestartAddr2LineProcess()
298

Member Data Documentation

◆ _lib_file_name

elf_symbolizer.ELFSymbolizer.Addr2Line._lib_file_name
protected

Definition at line 227 of file elf_symbolizer.py.

◆ _out_queue

elf_symbolizer.ELFSymbolizer.Addr2Line._out_queue
protected

Definition at line 244 of file elf_symbolizer.py.

◆ _proc

elf_symbolizer.ELFSymbolizer.Addr2Line._proc
protected

Definition at line 242 of file elf_symbolizer.py.

◆ _processed_symbols_count

elf_symbolizer.ELFSymbolizer.Addr2Line._processed_symbols_count
protected

Definition at line 239 of file elf_symbolizer.py.

◆ _request_queue

elf_symbolizer.ELFSymbolizer.Addr2Line._request_queue
protected

Definition at line 231 of file elf_symbolizer.py.

◆ _symbolizer

elf_symbolizer.ELFSymbolizer.Addr2Line._symbolizer
protected

Definition at line 226 of file elf_symbolizer.py.

◆ _thread

elf_symbolizer.ELFSymbolizer.Addr2Line._thread
protected

Definition at line 243 of file elf_symbolizer.py.

◆ queue_size

elf_symbolizer.ELFSymbolizer.Addr2Line.queue_size

Definition at line 235 of file elf_symbolizer.py.

◆ SYM_ADDR_RE

elf_symbolizer.ELFSymbolizer.Addr2Line.SYM_ADDR_RE = re.compile(r'([^:]+):(\?|\d+).*')
static

Definition at line 223 of file elf_symbolizer.py.


The documentation for this class was generated from the following file: