Start a perf process to profile this node. Returns the subprocess running perf.
(self, profile_name=None)
| 492 | self._stop_perf(profile_name) |
| 493 | |
| 494 | def _start_perf(self, profile_name=None): |
| 495 | """Start a perf process to profile this node. |
| 496 | |
| 497 | Returns the subprocess running perf.""" |
| 498 | subp = None |
| 499 | |
| 500 | def test_success(cmd): |
| 501 | return subprocess.call( |
| 502 | # shell=True required for pipe use below |
| 503 | cmd, shell=True, |
| 504 | stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL) == 0 |
| 505 | |
| 506 | if not sys.platform.startswith('linux'): |
| 507 | self.log.warning("Can't profile with perf; only available on Linux platforms") |
| 508 | return None |
| 509 | |
| 510 | if not test_success('which perf'): |
| 511 | self.log.warning("Can't profile with perf; must install perf-tools") |
| 512 | return None |
| 513 | |
| 514 | if not test_success('readelf -S {} | grep .debug_str'.format(shlex.quote(self.binary))): |
| 515 | self.log.warning( |
| 516 | "perf output won't be very useful without debug symbols compiled into bitcoind") |
| 517 | |
| 518 | output_path = tempfile.NamedTemporaryFile( |
| 519 | dir=self.datadir, |
| 520 | prefix="{}.perf.data.".format(profile_name or 'test'), |
| 521 | delete=False, |
| 522 | ).name |
| 523 | |
| 524 | cmd = [ |
| 525 | 'perf', 'record', |
| 526 | '-g', # Record the callgraph. |
| 527 | '--call-graph', 'dwarf', # Compatibility for gcc's --fomit-frame-pointer. |
| 528 | '-F', '101', # Sampling frequency in Hz. |
| 529 | '-p', str(self.process.pid), |
| 530 | '-o', output_path, |
| 531 | ] |
| 532 | subp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| 533 | self.perf_subprocesses[profile_name] = subp |
| 534 | |
| 535 | return subp |
| 536 | |
| 537 | def _stop_perf(self, profile_name): |
| 538 | """Stop (and pop) a perf subprocess.""" |
no test coverage detected