├── test ├── __init__.py └── test_net_http.py ├── pypprof ├── __init__.py ├── thread_profiler.py ├── index.html ├── builder.py ├── net_http.py ├── profile.proto └── profile_pb2.py ├── README.md ├── .gitignore ├── setup.py └── LICENSE /test/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pypprof/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pypprof/thread_profiler.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | import six 3 | import sys 4 | 5 | from pypprof import builder 6 | 7 | # Maximum stack frames to record. 8 | _MAX_STACK_DEPTH = 128 9 | 10 | 11 | def take_snapshot(): 12 | samples = {extract_trace(frame): (1, 1) 13 | for frame in six.itervalues(sys._current_frames())} 14 | profile_builder = builder.Builder() 15 | profile_builder.populate_profile(samples, 'THREADS', 'count', 1, 1) 16 | return profile_builder.emit() 17 | 18 | 19 | def extract_trace(frame): 20 | """Extracts the call stack trace of the given frame. 21 | 22 | Args: 23 | frame: A Frame object representing the leaf frame of the stack. 24 | 25 | Returns: 26 | A tuple of frames. The leaf frame is at position 0. A frame is a 27 | (function name, filename, line number) tuple. 28 | """ 29 | depth = 0 30 | trace = [] 31 | while frame is not None and depth < _MAX_STACK_DEPTH: 32 | frame_tuple = (frame.f_code.co_name, frame.f_code.co_filename, 33 | frame.f_code.co_firstlineno, frame.f_lineno) 34 | trace.append(frame_tuple) 35 | frame = frame.f_back 36 | depth += 1 37 | return tuple(trace) 38 | 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pypprof 2 | pypprof adds HTTP-based endpoints for collecting profiles from a running Python application a la Go's [`net/http/pprof`](https://golang.org/pkg/net/http/pprof/). 3 | 4 | Under the hood, it uses [zprofile](https://github.com/timpalpant/zprofile) and [mprofile](https://github.com/timpalpant/mprofile) to collect CPU and heap profiles with minimal overhead. 5 | 6 | # Usage 7 | 8 | ## Add profiling endpoints to an application 9 | 10 | Register the profiling endpoints in your application: 11 | ```python 12 | from pypprof.net_http import start_pprof_server 13 | 14 | start_pprof_server(port=8081) 15 | ``` 16 | 17 | ## Fetch profiles from your running application 18 | 19 | Fetch a 30s CPU profile, and view as a flamegraph: 20 | ```bash 21 | $ go tool pprof -http=:8088 :8081/debug/pprof/profile 22 | ``` 23 | 24 | Fetch a heap profile: 25 | ```bash 26 | $ go tool pprof :8081/debug/pprof/heap 27 | ``` 28 | 29 | Dump stacks from your application: 30 | ```bash 31 | $ curl localhost:8081/debug/pprof/thread?debug=1 32 | ``` 33 | 34 | # Compatibility 35 | 36 | pypprof is compatible with Python >= 2.7. Memory profiling is only available by default in Python >= 3.4. To enable memory profiling in earlier Pythons, you must patch Python and manually install [mprofile](https://github.com/timpalpant/mprofile). 37 | 38 | # Contributing 39 | 40 | Pull requests and issues are welcomed! 41 | 42 | # License 43 | 44 | pypprof is released under the [GNU Lesser General Public License, Version 3.0](https://www.gnu.org/licenses/lgpl-3.0.en.html) 45 | -------------------------------------------------------------------------------- /pypprof/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | /debug/pprof/ 4 | 10 | 11 | 12 | 13 | /debug/pprof/
14 |
15 | 16 | Types of profiles available: 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
CountProfile
0cmdline
{num_threads}thread
0heap
0profile
0wall
41 | 42 | full thread stack dump 43 |
44 | 45 |

46 | Profile Descriptions: 47 |

54 |

55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Bazel 2 | bazel-* 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | wheels/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | MANIFEST 30 | 31 | # PyInstaller 32 | # Usually these files are written by a python script from a template 33 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 34 | *.manifest 35 | *.spec 36 | 37 | # Installer logs 38 | pip-log.txt 39 | pip-delete-this-directory.txt 40 | 41 | # Unit test / coverage reports 42 | htmlcov/ 43 | .tox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | 53 | # Translations 54 | *.mo 55 | *.pot 56 | 57 | # Django stuff: 58 | *.log 59 | local_settings.py 60 | db.sqlite3 61 | 62 | # Flask stuff: 63 | instance/ 64 | .webassets-cache 65 | 66 | # Scrapy stuff: 67 | .scrapy 68 | 69 | # Sphinx documentation 70 | docs/_build/ 71 | 72 | # PyBuilder 73 | target/ 74 | 75 | # Jupyter Notebook 76 | .ipynb_checkpoints 77 | 78 | # pyenv 79 | .python-version 80 | 81 | # celery beat schedule file 82 | celerybeat-schedule 83 | 84 | # SageMath parsed files 85 | *.sage.py 86 | 87 | # Environments 88 | .env 89 | .venv 90 | env/ 91 | venv/ 92 | ENV/ 93 | env.bak/ 94 | venv.bak/ 95 | 96 | # Spyder project settings 97 | .spyderproject 98 | .spyproject 99 | 100 | # Rope project settings 101 | .ropeproject 102 | 103 | # mkdocs documentation 104 | /site 105 | 106 | # mypy 107 | .mypy_cache/ 108 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import io 2 | import os 3 | import sys 4 | 5 | from setuptools import setup 6 | 7 | README = os.path.join(os.path.abspath(os.path.dirname(__file__)), "README.md") 8 | with io.open(README, encoding="utf-8") as f: 9 | long_description = f.read() 10 | 11 | install_requires = ["protobuf", "six", "zprofile"] 12 | if sys.version_info.major >= 3: 13 | install_requires.append("mprofile") 14 | 15 | setup( 16 | name="pypprof", 17 | version="0.0.1", 18 | description="Python profiler endpoints like Go's net/http/pprof.", 19 | long_description=long_description, 20 | long_description_content_type="text/markdown", 21 | platforms=["Mac OS X", "POSIX"], 22 | classifiers=[ 23 | "Development Status :: 2 - Pre-Alpha", 24 | "Intended Audience :: Developers", 25 | "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)", 26 | "Operating System :: POSIX", 27 | "Programming Language :: Python :: 2.7", 28 | "Programming Language :: Python :: 3.4", 29 | "Programming Language :: Python :: 3.5", 30 | "Programming Language :: Python :: 3.6", 31 | "Programming Language :: Python :: 3.7", 32 | "Programming Language :: Python :: Implementation :: CPython", 33 | "Topic :: Software Development :: Testing", 34 | "Topic :: Software Development :: Libraries :: Python Modules" 35 | ], 36 | project_urls={ 37 | "Source": "https://github.com/timpalpant/pypprof", 38 | "Tracker": "https://github.com/timpalpant/pypprof/issues", 39 | }, 40 | keywords="profiling performance", 41 | url="http://github.com/timpalpant/pypprof", 42 | author="Timothy Palpant", 43 | author_email="tim@palpant.us", 44 | license="LGPLv3", 45 | packages=["pypprof"], 46 | package_data={"pypprof": ["index.html"]}, 47 | install_requires=install_requires, 48 | test_suite="test", 49 | ) 50 | -------------------------------------------------------------------------------- /test/test_net_http.py: -------------------------------------------------------------------------------- 1 | import gzip 2 | import io 3 | from six.moves.urllib.error import HTTPError 4 | from six.moves.urllib.request import urlopen 5 | import unittest 6 | 7 | import mprofile 8 | from pypprof.net_http import start_pprof_server 9 | from pypprof.profile_pb2 import Profile 10 | 11 | 12 | def parse_profile(buf): 13 | bufio = io.BytesIO(buf) 14 | with gzip.GzipFile(fileobj=bufio) as fd: 15 | return Profile.FromString(fd.read()) 16 | 17 | 18 | class TestPprofRequestHandler(unittest.TestCase): 19 | @classmethod 20 | def setUpClass(cls): 21 | mprofile.start(sample_rate=512) 22 | cls.host = "localhost" 23 | cls.port = 8083 24 | cls.server = start_pprof_server(cls.host, cls.port) 25 | 26 | @classmethod 27 | def tearDownClass(cls): 28 | cls.server.shutdown() 29 | mprofile.stop() 30 | 31 | def _make_request(self, route): 32 | url = "http://{}:{}/debug/pprof/{}".format( 33 | self.host, self.port, route) 34 | return urlopen(url) 35 | 36 | def test_index(self): 37 | resp = self._make_request("") 38 | body = resp.read().decode("utf-8") 39 | self.assertIn("Profile Descriptions:", body) 40 | 41 | def test_cmdline(self): 42 | resp = self._make_request("cmdline") 43 | body = resp.read().decode("utf-8") 44 | self.assertTrue(len(body) > 0) 45 | 46 | def test_profile(self): 47 | resp = self._make_request("profile?seconds=1") 48 | body = resp.read() 49 | profile = parse_profile(body) 50 | 51 | def test_wall(self): 52 | resp = self._make_request("wall?seconds=1") 53 | body = resp.read() 54 | profile = parse_profile(body) 55 | 56 | def test_thread(self): 57 | resp = self._make_request("thread") 58 | body = resp.read() 59 | profile = parse_profile(body) 60 | 61 | def test_thread_debug(self): 62 | resp = self._make_request("thread?debug=2") 63 | body = resp.read().decode("utf-8") 64 | self.assertIn("pypprof/net_http.py", body) 65 | self.assertIn("test/main.py", body) 66 | 67 | def test_heap(self): 68 | resp = self._make_request("heap") 69 | body = resp.read() 70 | profile = parse_profile(body) 71 | 72 | def test_heap_gc(self): 73 | resp = self._make_request("heap?gc=1") 74 | body = resp.read() 75 | profile = parse_profile(body) 76 | 77 | def test_404(self): 78 | with self.assertRaises(HTTPError): 79 | self._make_request("noexisto") 80 | 81 | 82 | if __name__ == '__main__': 83 | unittest.main() 84 | -------------------------------------------------------------------------------- /pypprof/builder.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Builds the profile proto from call stack traces.""" 16 | 17 | import collections 18 | import gzip 19 | import io 20 | import six 21 | 22 | from pypprof import profile_pb2 23 | 24 | Func = collections.namedtuple('Func', ['name', 'filename', 'start_line']) 25 | Loc = collections.namedtuple('Loc', ['func_id', 'line_number']) 26 | 27 | 28 | class Builder(object): 29 | """Builds the profile proto from call stack traces.""" 30 | 31 | def __init__(self): 32 | self._profile = profile_pb2.Profile() 33 | self._function_map = {} 34 | self._location_map = {} 35 | self._string_map = {} 36 | # string_table[0] in the profile proto must be an empty string. 37 | self._string_id('') 38 | 39 | def populate_profile(self, samples, profile_type, period_unit, period, 40 | duration_ns): 41 | """Populates call stack traces into a profile proto. 42 | 43 | Args: 44 | samples: A dict of trace => (count, measurement). A trace is a sequence of 45 | frames. The leaf frame is at trace[0]. A frame is represented as a tuple 46 | of (function name, filename, function start line, line number). 47 | profile_type: A string specifying the profile type, e.g 'CPU' or 'WALL'. 48 | See https://github.com/google/pprof/blob/master/proto/profile.proto for 49 | possible profile types. 50 | period_unit: A string specifying the measurement unit of the sampling 51 | period, e.g 'nanoseconds'. 52 | period: An integer specifying the interval between sampled occurrences. 53 | The measurement unit is specified by the period_unit argument. 54 | duration_ns: An integer specifying the profiling duration in nanoseconds. 55 | """ 56 | self._profile.period_type.type = self._string_id(profile_type) 57 | self._profile.period_type.unit = self._string_id(period_unit) 58 | self._profile.period = period 59 | self._profile.duration_nanos = duration_ns 60 | type1 = self._profile.sample_type.add() 61 | type1.type = self._string_id('sample') 62 | type1.unit = self._string_id('count') 63 | type2 = self._profile.sample_type.add() 64 | type2.type = self._string_id(profile_type) 65 | type2.unit = self._string_id(period_unit) 66 | 67 | for trace, (count, value) in six.iteritems(samples): 68 | sample = self._profile.sample.add() 69 | sample.value.append(count) 70 | sample.value.append(value) 71 | for frame in trace: 72 | # TODO: try to use named tuple for frame if it doesn't over 73 | # complicate the native profiler. 74 | func_id = self._function_id(frame[0], frame[1], frame[2]) 75 | location_id = self._location_id(func_id, frame[3]) 76 | sample.location_id.append(location_id) 77 | 78 | def emit(self): 79 | """Returns the profile in gzip-compressed profile proto format.""" 80 | profile = self._profile.SerializeToString() 81 | out = io.BytesIO() 82 | with gzip.GzipFile(fileobj=out, mode='wb') as f: 83 | f.write(profile) 84 | return out.getvalue() 85 | 86 | def _function_id(self, name, filename, start_line): 87 | """Finds the function ID in the proto, adds the function if not yet exists. 88 | 89 | Args: 90 | name: A string representing the function name. 91 | filename: A string representing the file name. 92 | start_line: The line number in filename on which this function starts. 93 | 94 | Returns: 95 | An integer representing the unique ID of the function in the profile 96 | proto. 97 | """ 98 | name_id = self._string_id(name) 99 | filename_id = self._string_id(filename) 100 | func = Func(name_id, filename_id, start_line) 101 | 102 | func_id = self._function_map.get(func) 103 | if func_id is None: 104 | # Function ID in profile proto must not be zero. 105 | func_id = len(self._function_map) + 1 106 | self._function_map[func] = func_id 107 | function = self._profile.function.add() 108 | function.name = name_id 109 | function.filename = filename_id 110 | function.start_line = start_line 111 | function.id = func_id 112 | return func_id 113 | 114 | def _location_id(self, func_id, line_number): 115 | """Finds the location ID in the proto, adds the location if not yet exists. 116 | 117 | Args: 118 | func_id: An integer representing the ID of the corresponding function in 119 | the profile proto. 120 | line_number: An integer representing the line number in the source code. 121 | 122 | Returns: 123 | An integer representing the unique ID of the location in the profile 124 | proto. 125 | """ 126 | loc = Loc(func_id=func_id, line_number=line_number) 127 | 128 | location_id = self._location_map.get(loc) 129 | if location_id is None: 130 | # Location ID in profile proto must not be zero. 131 | location_id = len(self._location_map) + 1 132 | self._location_map[loc] = location_id 133 | location = self._profile.location.add() 134 | location.id = location_id 135 | line = location.line.add() 136 | line.line = line_number 137 | line.function_id = func_id 138 | return location_id 139 | 140 | def _string_id(self, value): 141 | """Finds the string ID in the proto, adds the string if not yet exists.""" 142 | string_id = self._string_map.get(value) 143 | if string_id is None: 144 | string_id = len(self._string_map) 145 | self._string_map[value] = string_id 146 | self._profile.string_table.append(value) 147 | return string_id 148 | -------------------------------------------------------------------------------- /pypprof/net_http.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import gc 4 | import pkg_resources 5 | import sys 6 | import threading 7 | import time 8 | import traceback 9 | 10 | import six 11 | from six.moves.BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer 12 | from six.moves.urllib.parse import parse_qs, urlparse 13 | 14 | try: 15 | import mprofile 16 | has_mprofile = True 17 | except ImportError: 18 | has_mprofile = False 19 | 20 | from zprofile.cpu_profiler import CPUProfiler 21 | from zprofile.wall_profiler import WallProfiler 22 | from pypprof.builder import Builder 23 | from pypprof import thread_profiler 24 | 25 | 26 | _wall_profiler = WallProfiler() 27 | 28 | 29 | def start_pprof_server(host='localhost', port=8080): 30 | '''Start a pprof server at the given host:port in a background thread. 31 | 32 | After calling this function, the pprof endpoints should be available 33 | at /debug/pprof/profile, etc. 34 | 35 | Returns the underlying HTTPServer. To stop the server, call shutdown(). 36 | ''' 37 | # WallProfiler's registers a Python signal handler, which must be done 38 | # on the main thread. So do it now before spawning the background thread. 39 | # As a result, starting the pprof server has the side effect of registering the 40 | # wall-clock profiler's SIGALRM handler, which may conflict with other uses. 41 | _wall_profiler.register_handler() 42 | 43 | server = HTTPServer((host, port), PProfRequestHandler) 44 | bg_thread = threading.Thread(target=server.serve_forever) 45 | bg_thread.daemon = True 46 | bg_thread.start() 47 | return server 48 | 49 | 50 | class PProfRequestHandler(BaseHTTPRequestHandler): 51 | '''Handle pprof endpoint requests a la Go's net/http/pprof. 52 | 53 | The following endpoints are implemented: 54 | - /debug/pprof: List the available profiles. 55 | - /debug/pprof/profile: Collect a CPU profile. 56 | - /debug/pprof/wall: Collect a wall-clock profile. 57 | - /debug/pprof/heap: Get snapshot of current heap profile. 58 | - /debug/pprof/cmdline: The running program's command line. 59 | - /debug/pprof/thread (or /debug/pprof/goroutine): Currently running threads. 60 | ''' 61 | def do_GET(self): 62 | url = urlparse(self.path) 63 | route = url.path.rstrip("/") 64 | qs = parse_qs(url.query) 65 | 66 | if route == "/debug/pprof": 67 | self.index() 68 | elif route == "/debug/pprof/profile": 69 | self.profile(qs) 70 | elif route == "/debug/pprof/wall": 71 | self.wall(qs) 72 | elif route == "/debug/pprof/heap": 73 | self.heap(qs) 74 | elif route in ("/debug/pprof/thread", "/debug/pprof/goroutine"): 75 | self.thread(qs) 76 | elif route == "/debug/pprof/cmdline": 77 | self.cmdline() 78 | else: 79 | self.send_error(404) 80 | 81 | def index(self): 82 | template = pkg_resources.resource_string(__name__, "index.html").decode("utf-8") 83 | body = template.format(num_threads=threading.active_count()) 84 | 85 | self.send_response(200) 86 | self.send_header("X-Content-Type-Options", "nosniff") 87 | self.send_header("Content-Type", "text/plain; charset=utf-8") 88 | self.end_headers() 89 | self.wfile.write(body.encode("utf-8")) 90 | 91 | def profile(self, query): 92 | duration_qs = query.get("seconds", [30]) 93 | duration_secs = int(duration_qs[0]) 94 | cpu_profiler = CPUProfiler() 95 | pprof = cpu_profiler.profile(duration_secs) 96 | self._send_profile(pprof) 97 | 98 | def wall(self, query): 99 | duration_qs = query.get("seconds", [30]) 100 | duration_secs = int(duration_qs[0]) 101 | pprof = _wall_profiler.profile(duration_secs) 102 | self._send_profile(pprof) 103 | 104 | def heap(self, query): 105 | if query.get("gc"): 106 | gc.collect() 107 | if not has_mprofile: 108 | return self.send_error(412, "mprofile must be installed to enable heap profiling") 109 | if not mprofile.is_tracing(): 110 | return self.send_error(412, "Heap profiling is not enabled") 111 | snap = mprofile.take_snapshot() 112 | pprof = build_heap_pprof(snap) 113 | self._send_profile(pprof) 114 | 115 | def thread(self, query): 116 | if query.get("debug"): 117 | self.send_response(200) 118 | self.send_header("X-Content-Type-Options", "nosniff") 119 | self.send_header("Content-Type", "text/plain; charset=utf-8") 120 | self.end_headers() 121 | for frame in six.itervalues(sys._current_frames()): 122 | for line in traceback.format_stack(frame): 123 | self.wfile.write(line.encode("utf-8")) 124 | self.wfile.write("\n".encode("utf-8")) 125 | else: 126 | pprof = thread_profiler.take_snapshot() 127 | self._send_profile(pprof) 128 | 129 | def cmdline(self): 130 | body = "\0".join(sys.argv) 131 | self.send_response(200) 132 | self.send_header("X-Content-Type-Options", "nosniff") 133 | self.send_header("Content-Type", "text/plain; charset=utf-8") 134 | self.end_headers() 135 | self.wfile.write(body.encode("utf-8")) 136 | 137 | def _send_profile(self, pprof): 138 | self.send_response(200) 139 | self.send_header("Content-Type", "application/octet-stream") 140 | self.send_header("Content-Disposition", 'attachment; filename="profile"') 141 | self.end_headers() 142 | self.wfile.write(pprof) 143 | 144 | 145 | def build_heap_pprof(snap): 146 | profile_builder = Builder() 147 | samples = {} # trace => (count, measurement) 148 | for stat in snap.statistics('traceback'): 149 | trace = tuple((frame.name, frame.filename, frame.firstlineno, frame.lineno) 150 | for frame in stat.traceback) 151 | try: 152 | samples[trace][0] += stat.count 153 | samples[trace][1] += stat.size 154 | except KeyError: 155 | samples[trace] = (stat.count, stat.size) 156 | profile_builder.populate_profile(samples, 'HEAP', 'bytes', snap.sample_rate, 1) 157 | return profile_builder.emit() 158 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /pypprof/profile.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Google Inc. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Profile is a common stacktrace profile format. 16 | // 17 | // Measurements represented with this format should follow the 18 | // following conventions: 19 | // 20 | // - Consumers should treat unset optional fields as if they had been 21 | // set with their default value. 22 | // 23 | // - When possible, measurements should be stored in "unsampled" form 24 | // that is most useful to humans. There should be enough 25 | // information present to determine the original sampled values. 26 | // 27 | // - On-disk, the serialized proto must be gzip-compressed. 28 | // 29 | // - The profile is represented as a set of samples, where each sample 30 | // references a sequence of locations, and where each location belongs 31 | // to a mapping. 32 | // - There is a N->1 relationship from sample.location_id entries to 33 | // locations. For every sample.location_id entry there must be a 34 | // unique Location with that id. 35 | // - There is an optional N->1 relationship from locations to 36 | // mappings. For every nonzero Location.mapping_id there must be a 37 | // unique Mapping with that id. 38 | 39 | syntax = "proto3"; 40 | 41 | package perftools.profiles; 42 | 43 | option java_package = "com.google.perftools.profiles"; 44 | option java_outer_classname = "ProfileProto"; 45 | 46 | message Profile { 47 | // A description of the samples associated with each Sample.value. 48 | // For a cpu profile this might be: 49 | // [["cpu","nanoseconds"]] or [["wall","seconds"]] or [["syscall","count"]] 50 | // For a heap profile, this might be: 51 | // [["allocations","count"], ["space","bytes"]], 52 | // If one of the values represents the number of events represented 53 | // by the sample, by convention it should be at index 0 and use 54 | // sample_type.unit == "count". 55 | repeated ValueType sample_type = 1; 56 | // The set of samples recorded in this profile. 57 | repeated Sample sample = 2; 58 | // Mapping from address ranges to the image/binary/library mapped 59 | // into that address range. mapping[0] will be the main binary. 60 | repeated Mapping mapping = 3; 61 | // Useful program location 62 | repeated Location location = 4; 63 | // Functions referenced by locations 64 | repeated Function function = 5; 65 | // A common table for strings referenced by various messages. 66 | // string_table[0] must always be "". 67 | repeated string string_table = 6; 68 | // frames with Function.function_name fully matching the following 69 | // regexp will be dropped from the samples, along with their successors. 70 | int64 drop_frames = 7; // Index into string table. 71 | // frames with Function.function_name fully matching the following 72 | // regexp will be kept, even if it matches drop_functions. 73 | int64 keep_frames = 8; // Index into string table. 74 | 75 | // The following fields are informational, do not affect 76 | // interpretation of results. 77 | 78 | // Time of collection (UTC) represented as nanoseconds past the epoch. 79 | int64 time_nanos = 9; 80 | // Duration of the profile, if a duration makes sense. 81 | int64 duration_nanos = 10; 82 | // The kind of events between sampled ocurrences. 83 | // e.g [ "cpu","cycles" ] or [ "heap","bytes" ] 84 | ValueType period_type = 11; 85 | // The number of events between sampled occurrences. 86 | int64 period = 12; 87 | // Freeform text associated to the profile. 88 | repeated int64 comment = 13; // Indices into string table. 89 | // Index into the string table of the type of the preferred sample 90 | // value. If unset, clients should default to the last sample value. 91 | int64 default_sample_type = 14; 92 | } 93 | 94 | // ValueType describes the semantics and measurement units of a value. 95 | message ValueType { 96 | int64 type = 1; // Index into string table. 97 | int64 unit = 2; // Index into string table. 98 | } 99 | 100 | // Each Sample records values encountered in some program 101 | // context. The program context is typically a stack trace, perhaps 102 | // augmented with auxiliary information like the thread-id, some 103 | // indicator of a higher level request being handled etc. 104 | message Sample { 105 | // The ids recorded here correspond to a Profile.location.id. 106 | // The leaf is at location_id[0]. 107 | repeated uint64 location_id = 1; 108 | // The type and unit of each value is defined by the corresponding 109 | // entry in Profile.sample_type. All samples must have the same 110 | // number of values, the same as the length of Profile.sample_type. 111 | // When aggregating multiple samples into a single sample, the 112 | // result has a list of values that is the elemntwise sum of the 113 | // lists of the originals. 114 | repeated int64 value = 2; 115 | // label includes additional context for this sample. It can include 116 | // things like a thread id, allocation size, etc 117 | repeated Label label = 3; 118 | } 119 | 120 | message Label { 121 | int64 key = 1; // Index into string table 122 | 123 | // At most one of the following must be present 124 | int64 str = 2; // Index into string table 125 | int64 num = 3; 126 | 127 | // Should only be present when num is present. 128 | // Specifies the units of num. 129 | // Use arbitrary string (for example, "requests") as a custom count unit. 130 | // If no unit is specified, consumer may apply heuristic to deduce the unit. 131 | // Consumers may also interpret units like "bytes" and "kilobytes" as memory 132 | // units and units like "seconds" and "nanoseconds" as time units, 133 | // and apply appropriate unit conversions to these. 134 | int64 num_unit = 4; // Index into string table 135 | } 136 | 137 | message Mapping { 138 | // Unique nonzero id for the mapping. 139 | uint64 id = 1; 140 | // Address at which the binary (or DLL) is loaded into memory. 141 | uint64 memory_start = 2; 142 | // The limit of the address range occupied by this mapping. 143 | uint64 memory_limit = 3; 144 | // Offset in the binary that corresponds to the first mapped address. 145 | uint64 file_offset = 4; 146 | // The object this entry is loaded from. This can be a filename on 147 | // disk for the main binary and shared libraries, or virtual 148 | // abstractions like "[vdso]". 149 | int64 filename = 5; // Index into string table 150 | // A string that uniquely identifies a particular program version 151 | // with high probability. E.g., for binaries generated by GNU tools, 152 | // it could be the contents of the .note.gnu.build-id field. 153 | int64 build_id = 6; // Index into string table 154 | 155 | // The following fields indicate the resolution of symbolic info. 156 | bool has_functions = 7; 157 | bool has_filenames = 8; 158 | bool has_line_numbers = 9; 159 | bool has_inline_frames = 10; 160 | } 161 | 162 | // Describes function and line table debug information. 163 | message Location { 164 | // Unique nonzero id for the location. A profile could use 165 | // instruction addresses or any integer sequence as ids. 166 | uint64 id = 1; 167 | // The id of the corresponding profile.Mapping for this location. 168 | // It can be unset if the mapping is unknown or not applicable for 169 | // this profile type. 170 | uint64 mapping_id = 2; 171 | // The instruction address for this location, if available. It 172 | // should be within [Mapping.memory_start...Mapping.memory_limit] 173 | // for the corresponding mapping. A non-leaf address may be in the 174 | // middle of a call instruction. It is up to display tools to find 175 | // the beginning of the instruction if necessary. 176 | uint64 address = 3; 177 | // Multiple line indicates this location has inlined functions, 178 | // where the last entry represents the caller into which the 179 | // preceding entries were inlined. 180 | // 181 | // E.g., if memcpy() is inlined into printf: 182 | // line[0].function_name == "memcpy" 183 | // line[1].function_name == "printf" 184 | repeated Line line = 4; 185 | // Provides an indication that multiple symbols map to this location's 186 | // address, for example due to identical code folding by the linker. In that 187 | // case the line information above represents one of the multiple 188 | // symbols. This field must be recomputed when the symbolization state of the 189 | // profile changes. 190 | bool is_folded = 5; 191 | } 192 | 193 | message Line { 194 | // The id of the corresponding profile.Function for this line. 195 | uint64 function_id = 1; 196 | // Line number in source code. 197 | int64 line = 2; 198 | } 199 | 200 | message Function { 201 | // Unique nonzero id for the function. 202 | uint64 id = 1; 203 | // Name of the function, in human-readable form if available. 204 | int64 name = 2; // Index into string table 205 | // Name of the function, as identified by the system. 206 | // For instance, it can be a C++ mangled name. 207 | int64 system_name = 3; // Index into string table 208 | // Source file containing the function. 209 | int64 filename = 4; // Index into string table 210 | // Line number in source file. 211 | int64 start_line = 5; 212 | } 213 | -------------------------------------------------------------------------------- /pypprof/profile_pb2.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Generated by the protocol buffer compiler. DO NOT EDIT! 16 | # source: profile.proto 17 | # pylint:skip-file 18 | 19 | import sys 20 | _b = sys.version_info[0] < 3 and (lambda x: x) or (lambda x: x.encode('latin1')) 21 | from google.protobuf import descriptor as _descriptor 22 | from google.protobuf import message as _message 23 | from google.protobuf import reflection as _reflection 24 | from google.protobuf import symbol_database as _symbol_database 25 | from google.protobuf import descriptor_pb2 26 | # @@protoc_insertion_point(imports) 27 | 28 | _sym_db = _symbol_database.Default() 29 | 30 | DESCRIPTOR = _descriptor.FileDescriptor( 31 | name='profile.proto', 32 | package='perftools.profiles', 33 | syntax='proto3', 34 | serialized_pb=_b( 35 | '\n\rprofile.proto\x12\x12perftools.profiles\"\xd5\x03\n\x07Profile\x12\x32\n\x0bsample_type\x18\x01 \x03(\x0b\x32\x1d.perftools.profiles.ValueType\x12*\n\x06sample\x18\x02 \x03(\x0b\x32\x1a.perftools.profiles.Sample\x12,\n\x07mapping\x18\x03 \x03(\x0b\x32\x1b.perftools.profiles.Mapping\x12.\n\x08location\x18\x04 \x03(\x0b\x32\x1c.perftools.profiles.Location\x12.\n\x08\x66unction\x18\x05 \x03(\x0b\x32\x1c.perftools.profiles.Function\x12\x14\n\x0cstring_table\x18\x06 \x03(\t\x12\x13\n\x0b\x64rop_frames\x18\x07 \x01(\x03\x12\x13\n\x0bkeep_frames\x18\x08 \x01(\x03\x12\x12\n\ntime_nanos\x18\t \x01(\x03\x12\x16\n\x0e\x64uration_nanos\x18\n \x01(\x03\x12\x32\n\x0bperiod_type\x18\x0b \x01(\x0b\x32\x1d.perftools.profiles.ValueType\x12\x0e\n\x06period\x18\x0c \x01(\x03\x12\x0f\n\x07\x63omment\x18\r \x03(\x03\x12\x1b\n\x13\x64\x65\x66\x61ult_sample_type\x18\x0e \x01(\x03\"\'\n\tValueType\x12\x0c\n\x04type\x18\x01 \x01(\x03\x12\x0c\n\x04unit\x18\x02 \x01(\x03\"V\n\x06Sample\x12\x13\n\x0blocation_id\x18\x01 \x03(\x04\x12\r\n\x05value\x18\x02 \x03(\x03\x12(\n\x05label\x18\x03 \x03(\x0b\x32\x19.perftools.profiles.Label\"@\n\x05Label\x12\x0b\n\x03key\x18\x01 \x01(\x03\x12\x0b\n\x03str\x18\x02 \x01(\x03\x12\x0b\n\x03num\x18\x03 \x01(\x03\x12\x10\n\x08num_unit\x18\x04 \x01(\x03\"\xdd\x01\n\x07Mapping\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x14\n\x0cmemory_start\x18\x02 \x01(\x04\x12\x14\n\x0cmemory_limit\x18\x03 \x01(\x04\x12\x13\n\x0b\x66ile_offset\x18\x04 \x01(\x04\x12\x10\n\x08\x66ilename\x18\x05 \x01(\x03\x12\x10\n\x08\x62uild_id\x18\x06 \x01(\x03\x12\x15\n\rhas_functions\x18\x07 \x01(\x08\x12\x15\n\rhas_filenames\x18\x08 \x01(\x08\x12\x18\n\x10has_line_numbers\x18\t \x01(\x08\x12\x19\n\x11has_inline_frames\x18\n \x01(\x08\"v\n\x08Location\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x12\n\nmapping_id\x18\x02 \x01(\x04\x12\x0f\n\x07\x61\x64\x64ress\x18\x03 \x01(\x04\x12&\n\x04line\x18\x04 \x03(\x0b\x32\x18.perftools.profiles.Line\x12\x11\n\tis_folded\x18\x05 \x01(\x08\")\n\x04Line\x12\x13\n\x0b\x66unction_id\x18\x01 \x01(\x04\x12\x0c\n\x04line\x18\x02 \x01(\x03\"_\n\x08\x46unction\x12\n\n\x02id\x18\x01 \x01(\x04\x12\x0c\n\x04name\x18\x02 \x01(\x03\x12\x13\n\x0bsystem_name\x18\x03 \x01(\x03\x12\x10\n\x08\x66ilename\x18\x04 \x01(\x03\x12\x12\n\nstart_line\x18\x05 \x01(\x03\x42-\n\x1d\x63om.google.perftools.profilesB\x0cProfileProtob\x06proto3' 36 | )) 37 | _sym_db.RegisterFileDescriptor(DESCRIPTOR) 38 | 39 | _PROFILE = _descriptor.Descriptor( 40 | name='Profile', 41 | full_name='perftools.profiles.Profile', 42 | filename=None, 43 | file=DESCRIPTOR, 44 | containing_type=None, 45 | fields=[ 46 | _descriptor.FieldDescriptor( 47 | name='sample_type', 48 | full_name='perftools.profiles.Profile.sample_type', 49 | index=0, 50 | number=1, 51 | type=11, 52 | cpp_type=10, 53 | label=3, 54 | has_default_value=False, 55 | default_value=[], 56 | message_type=None, 57 | enum_type=None, 58 | containing_type=None, 59 | is_extension=False, 60 | extension_scope=None, 61 | options=None), 62 | _descriptor.FieldDescriptor( 63 | name='sample', 64 | full_name='perftools.profiles.Profile.sample', 65 | index=1, 66 | number=2, 67 | type=11, 68 | cpp_type=10, 69 | label=3, 70 | has_default_value=False, 71 | default_value=[], 72 | message_type=None, 73 | enum_type=None, 74 | containing_type=None, 75 | is_extension=False, 76 | extension_scope=None, 77 | options=None), 78 | _descriptor.FieldDescriptor( 79 | name='mapping', 80 | full_name='perftools.profiles.Profile.mapping', 81 | index=2, 82 | number=3, 83 | type=11, 84 | cpp_type=10, 85 | label=3, 86 | has_default_value=False, 87 | default_value=[], 88 | message_type=None, 89 | enum_type=None, 90 | containing_type=None, 91 | is_extension=False, 92 | extension_scope=None, 93 | options=None), 94 | _descriptor.FieldDescriptor( 95 | name='location', 96 | full_name='perftools.profiles.Profile.location', 97 | index=3, 98 | number=4, 99 | type=11, 100 | cpp_type=10, 101 | label=3, 102 | has_default_value=False, 103 | default_value=[], 104 | message_type=None, 105 | enum_type=None, 106 | containing_type=None, 107 | is_extension=False, 108 | extension_scope=None, 109 | options=None), 110 | _descriptor.FieldDescriptor( 111 | name='function', 112 | full_name='perftools.profiles.Profile.function', 113 | index=4, 114 | number=5, 115 | type=11, 116 | cpp_type=10, 117 | label=3, 118 | has_default_value=False, 119 | default_value=[], 120 | message_type=None, 121 | enum_type=None, 122 | containing_type=None, 123 | is_extension=False, 124 | extension_scope=None, 125 | options=None), 126 | _descriptor.FieldDescriptor( 127 | name='string_table', 128 | full_name='perftools.profiles.Profile.string_table', 129 | index=5, 130 | number=6, 131 | type=9, 132 | cpp_type=9, 133 | label=3, 134 | has_default_value=False, 135 | default_value=[], 136 | message_type=None, 137 | enum_type=None, 138 | containing_type=None, 139 | is_extension=False, 140 | extension_scope=None, 141 | options=None), 142 | _descriptor.FieldDescriptor( 143 | name='drop_frames', 144 | full_name='perftools.profiles.Profile.drop_frames', 145 | index=6, 146 | number=7, 147 | type=3, 148 | cpp_type=2, 149 | label=1, 150 | has_default_value=False, 151 | default_value=0, 152 | message_type=None, 153 | enum_type=None, 154 | containing_type=None, 155 | is_extension=False, 156 | extension_scope=None, 157 | options=None), 158 | _descriptor.FieldDescriptor( 159 | name='keep_frames', 160 | full_name='perftools.profiles.Profile.keep_frames', 161 | index=7, 162 | number=8, 163 | type=3, 164 | cpp_type=2, 165 | label=1, 166 | has_default_value=False, 167 | default_value=0, 168 | message_type=None, 169 | enum_type=None, 170 | containing_type=None, 171 | is_extension=False, 172 | extension_scope=None, 173 | options=None), 174 | _descriptor.FieldDescriptor( 175 | name='time_nanos', 176 | full_name='perftools.profiles.Profile.time_nanos', 177 | index=8, 178 | number=9, 179 | type=3, 180 | cpp_type=2, 181 | label=1, 182 | has_default_value=False, 183 | default_value=0, 184 | message_type=None, 185 | enum_type=None, 186 | containing_type=None, 187 | is_extension=False, 188 | extension_scope=None, 189 | options=None), 190 | _descriptor.FieldDescriptor( 191 | name='duration_nanos', 192 | full_name='perftools.profiles.Profile.duration_nanos', 193 | index=9, 194 | number=10, 195 | type=3, 196 | cpp_type=2, 197 | label=1, 198 | has_default_value=False, 199 | default_value=0, 200 | message_type=None, 201 | enum_type=None, 202 | containing_type=None, 203 | is_extension=False, 204 | extension_scope=None, 205 | options=None), 206 | _descriptor.FieldDescriptor( 207 | name='period_type', 208 | full_name='perftools.profiles.Profile.period_type', 209 | index=10, 210 | number=11, 211 | type=11, 212 | cpp_type=10, 213 | label=1, 214 | has_default_value=False, 215 | default_value=None, 216 | message_type=None, 217 | enum_type=None, 218 | containing_type=None, 219 | is_extension=False, 220 | extension_scope=None, 221 | options=None), 222 | _descriptor.FieldDescriptor( 223 | name='period', 224 | full_name='perftools.profiles.Profile.period', 225 | index=11, 226 | number=12, 227 | type=3, 228 | cpp_type=2, 229 | label=1, 230 | has_default_value=False, 231 | default_value=0, 232 | message_type=None, 233 | enum_type=None, 234 | containing_type=None, 235 | is_extension=False, 236 | extension_scope=None, 237 | options=None), 238 | _descriptor.FieldDescriptor( 239 | name='comment', 240 | full_name='perftools.profiles.Profile.comment', 241 | index=12, 242 | number=13, 243 | type=3, 244 | cpp_type=2, 245 | label=3, 246 | has_default_value=False, 247 | default_value=[], 248 | message_type=None, 249 | enum_type=None, 250 | containing_type=None, 251 | is_extension=False, 252 | extension_scope=None, 253 | options=None), 254 | _descriptor.FieldDescriptor( 255 | name='default_sample_type', 256 | full_name='perftools.profiles.Profile.default_sample_type', 257 | index=13, 258 | number=14, 259 | type=3, 260 | cpp_type=2, 261 | label=1, 262 | has_default_value=False, 263 | default_value=0, 264 | message_type=None, 265 | enum_type=None, 266 | containing_type=None, 267 | is_extension=False, 268 | extension_scope=None, 269 | options=None), 270 | ], 271 | extensions=[], 272 | nested_types=[], 273 | enum_types=[], 274 | options=None, 275 | is_extendable=False, 276 | syntax='proto3', 277 | extension_ranges=[], 278 | oneofs=[], 279 | serialized_start=38, 280 | serialized_end=507, 281 | ) 282 | 283 | _VALUETYPE = _descriptor.Descriptor( 284 | name='ValueType', 285 | full_name='perftools.profiles.ValueType', 286 | filename=None, 287 | file=DESCRIPTOR, 288 | containing_type=None, 289 | fields=[ 290 | _descriptor.FieldDescriptor( 291 | name='type', 292 | full_name='perftools.profiles.ValueType.type', 293 | index=0, 294 | number=1, 295 | type=3, 296 | cpp_type=2, 297 | label=1, 298 | has_default_value=False, 299 | default_value=0, 300 | message_type=None, 301 | enum_type=None, 302 | containing_type=None, 303 | is_extension=False, 304 | extension_scope=None, 305 | options=None), 306 | _descriptor.FieldDescriptor( 307 | name='unit', 308 | full_name='perftools.profiles.ValueType.unit', 309 | index=1, 310 | number=2, 311 | type=3, 312 | cpp_type=2, 313 | label=1, 314 | has_default_value=False, 315 | default_value=0, 316 | message_type=None, 317 | enum_type=None, 318 | containing_type=None, 319 | is_extension=False, 320 | extension_scope=None, 321 | options=None), 322 | ], 323 | extensions=[], 324 | nested_types=[], 325 | enum_types=[], 326 | options=None, 327 | is_extendable=False, 328 | syntax='proto3', 329 | extension_ranges=[], 330 | oneofs=[], 331 | serialized_start=509, 332 | serialized_end=548, 333 | ) 334 | 335 | _SAMPLE = _descriptor.Descriptor( 336 | name='Sample', 337 | full_name='perftools.profiles.Sample', 338 | filename=None, 339 | file=DESCRIPTOR, 340 | containing_type=None, 341 | fields=[ 342 | _descriptor.FieldDescriptor( 343 | name='location_id', 344 | full_name='perftools.profiles.Sample.location_id', 345 | index=0, 346 | number=1, 347 | type=4, 348 | cpp_type=4, 349 | label=3, 350 | has_default_value=False, 351 | default_value=[], 352 | message_type=None, 353 | enum_type=None, 354 | containing_type=None, 355 | is_extension=False, 356 | extension_scope=None, 357 | options=None), 358 | _descriptor.FieldDescriptor( 359 | name='value', 360 | full_name='perftools.profiles.Sample.value', 361 | index=1, 362 | number=2, 363 | type=3, 364 | cpp_type=2, 365 | label=3, 366 | has_default_value=False, 367 | default_value=[], 368 | message_type=None, 369 | enum_type=None, 370 | containing_type=None, 371 | is_extension=False, 372 | extension_scope=None, 373 | options=None), 374 | _descriptor.FieldDescriptor( 375 | name='label', 376 | full_name='perftools.profiles.Sample.label', 377 | index=2, 378 | number=3, 379 | type=11, 380 | cpp_type=10, 381 | label=3, 382 | has_default_value=False, 383 | default_value=[], 384 | message_type=None, 385 | enum_type=None, 386 | containing_type=None, 387 | is_extension=False, 388 | extension_scope=None, 389 | options=None), 390 | ], 391 | extensions=[], 392 | nested_types=[], 393 | enum_types=[], 394 | options=None, 395 | is_extendable=False, 396 | syntax='proto3', 397 | extension_ranges=[], 398 | oneofs=[], 399 | serialized_start=550, 400 | serialized_end=636, 401 | ) 402 | 403 | _LABEL = _descriptor.Descriptor( 404 | name='Label', 405 | full_name='perftools.profiles.Label', 406 | filename=None, 407 | file=DESCRIPTOR, 408 | containing_type=None, 409 | fields=[ 410 | _descriptor.FieldDescriptor( 411 | name='key', 412 | full_name='perftools.profiles.Label.key', 413 | index=0, 414 | number=1, 415 | type=3, 416 | cpp_type=2, 417 | label=1, 418 | has_default_value=False, 419 | default_value=0, 420 | message_type=None, 421 | enum_type=None, 422 | containing_type=None, 423 | is_extension=False, 424 | extension_scope=None, 425 | options=None), 426 | _descriptor.FieldDescriptor( 427 | name='str', 428 | full_name='perftools.profiles.Label.str', 429 | index=1, 430 | number=2, 431 | type=3, 432 | cpp_type=2, 433 | label=1, 434 | has_default_value=False, 435 | default_value=0, 436 | message_type=None, 437 | enum_type=None, 438 | containing_type=None, 439 | is_extension=False, 440 | extension_scope=None, 441 | options=None), 442 | _descriptor.FieldDescriptor( 443 | name='num', 444 | full_name='perftools.profiles.Label.num', 445 | index=2, 446 | number=3, 447 | type=3, 448 | cpp_type=2, 449 | label=1, 450 | has_default_value=False, 451 | default_value=0, 452 | message_type=None, 453 | enum_type=None, 454 | containing_type=None, 455 | is_extension=False, 456 | extension_scope=None, 457 | options=None), 458 | _descriptor.FieldDescriptor( 459 | name='num_unit', 460 | full_name='perftools.profiles.Label.num_unit', 461 | index=3, 462 | number=4, 463 | type=3, 464 | cpp_type=2, 465 | label=1, 466 | has_default_value=False, 467 | default_value=0, 468 | message_type=None, 469 | enum_type=None, 470 | containing_type=None, 471 | is_extension=False, 472 | extension_scope=None, 473 | options=None), 474 | ], 475 | extensions=[], 476 | nested_types=[], 477 | enum_types=[], 478 | options=None, 479 | is_extendable=False, 480 | syntax='proto3', 481 | extension_ranges=[], 482 | oneofs=[], 483 | serialized_start=638, 484 | serialized_end=702, 485 | ) 486 | 487 | _MAPPING = _descriptor.Descriptor( 488 | name='Mapping', 489 | full_name='perftools.profiles.Mapping', 490 | filename=None, 491 | file=DESCRIPTOR, 492 | containing_type=None, 493 | fields=[ 494 | _descriptor.FieldDescriptor( 495 | name='id', 496 | full_name='perftools.profiles.Mapping.id', 497 | index=0, 498 | number=1, 499 | type=4, 500 | cpp_type=4, 501 | label=1, 502 | has_default_value=False, 503 | default_value=0, 504 | message_type=None, 505 | enum_type=None, 506 | containing_type=None, 507 | is_extension=False, 508 | extension_scope=None, 509 | options=None), 510 | _descriptor.FieldDescriptor( 511 | name='memory_start', 512 | full_name='perftools.profiles.Mapping.memory_start', 513 | index=1, 514 | number=2, 515 | type=4, 516 | cpp_type=4, 517 | label=1, 518 | has_default_value=False, 519 | default_value=0, 520 | message_type=None, 521 | enum_type=None, 522 | containing_type=None, 523 | is_extension=False, 524 | extension_scope=None, 525 | options=None), 526 | _descriptor.FieldDescriptor( 527 | name='memory_limit', 528 | full_name='perftools.profiles.Mapping.memory_limit', 529 | index=2, 530 | number=3, 531 | type=4, 532 | cpp_type=4, 533 | label=1, 534 | has_default_value=False, 535 | default_value=0, 536 | message_type=None, 537 | enum_type=None, 538 | containing_type=None, 539 | is_extension=False, 540 | extension_scope=None, 541 | options=None), 542 | _descriptor.FieldDescriptor( 543 | name='file_offset', 544 | full_name='perftools.profiles.Mapping.file_offset', 545 | index=3, 546 | number=4, 547 | type=4, 548 | cpp_type=4, 549 | label=1, 550 | has_default_value=False, 551 | default_value=0, 552 | message_type=None, 553 | enum_type=None, 554 | containing_type=None, 555 | is_extension=False, 556 | extension_scope=None, 557 | options=None), 558 | _descriptor.FieldDescriptor( 559 | name='filename', 560 | full_name='perftools.profiles.Mapping.filename', 561 | index=4, 562 | number=5, 563 | type=3, 564 | cpp_type=2, 565 | label=1, 566 | has_default_value=False, 567 | default_value=0, 568 | message_type=None, 569 | enum_type=None, 570 | containing_type=None, 571 | is_extension=False, 572 | extension_scope=None, 573 | options=None), 574 | _descriptor.FieldDescriptor( 575 | name='build_id', 576 | full_name='perftools.profiles.Mapping.build_id', 577 | index=5, 578 | number=6, 579 | type=3, 580 | cpp_type=2, 581 | label=1, 582 | has_default_value=False, 583 | default_value=0, 584 | message_type=None, 585 | enum_type=None, 586 | containing_type=None, 587 | is_extension=False, 588 | extension_scope=None, 589 | options=None), 590 | _descriptor.FieldDescriptor( 591 | name='has_functions', 592 | full_name='perftools.profiles.Mapping.has_functions', 593 | index=6, 594 | number=7, 595 | type=8, 596 | cpp_type=7, 597 | label=1, 598 | has_default_value=False, 599 | default_value=False, 600 | message_type=None, 601 | enum_type=None, 602 | containing_type=None, 603 | is_extension=False, 604 | extension_scope=None, 605 | options=None), 606 | _descriptor.FieldDescriptor( 607 | name='has_filenames', 608 | full_name='perftools.profiles.Mapping.has_filenames', 609 | index=7, 610 | number=8, 611 | type=8, 612 | cpp_type=7, 613 | label=1, 614 | has_default_value=False, 615 | default_value=False, 616 | message_type=None, 617 | enum_type=None, 618 | containing_type=None, 619 | is_extension=False, 620 | extension_scope=None, 621 | options=None), 622 | _descriptor.FieldDescriptor( 623 | name='has_line_numbers', 624 | full_name='perftools.profiles.Mapping.has_line_numbers', 625 | index=8, 626 | number=9, 627 | type=8, 628 | cpp_type=7, 629 | label=1, 630 | has_default_value=False, 631 | default_value=False, 632 | message_type=None, 633 | enum_type=None, 634 | containing_type=None, 635 | is_extension=False, 636 | extension_scope=None, 637 | options=None), 638 | _descriptor.FieldDescriptor( 639 | name='has_inline_frames', 640 | full_name='perftools.profiles.Mapping.has_inline_frames', 641 | index=9, 642 | number=10, 643 | type=8, 644 | cpp_type=7, 645 | label=1, 646 | has_default_value=False, 647 | default_value=False, 648 | message_type=None, 649 | enum_type=None, 650 | containing_type=None, 651 | is_extension=False, 652 | extension_scope=None, 653 | options=None), 654 | ], 655 | extensions=[], 656 | nested_types=[], 657 | enum_types=[], 658 | options=None, 659 | is_extendable=False, 660 | syntax='proto3', 661 | extension_ranges=[], 662 | oneofs=[], 663 | serialized_start=705, 664 | serialized_end=926, 665 | ) 666 | 667 | _LOCATION = _descriptor.Descriptor( 668 | name='Location', 669 | full_name='perftools.profiles.Location', 670 | filename=None, 671 | file=DESCRIPTOR, 672 | containing_type=None, 673 | fields=[ 674 | _descriptor.FieldDescriptor( 675 | name='id', 676 | full_name='perftools.profiles.Location.id', 677 | index=0, 678 | number=1, 679 | type=4, 680 | cpp_type=4, 681 | label=1, 682 | has_default_value=False, 683 | default_value=0, 684 | message_type=None, 685 | enum_type=None, 686 | containing_type=None, 687 | is_extension=False, 688 | extension_scope=None, 689 | options=None), 690 | _descriptor.FieldDescriptor( 691 | name='mapping_id', 692 | full_name='perftools.profiles.Location.mapping_id', 693 | index=1, 694 | number=2, 695 | type=4, 696 | cpp_type=4, 697 | label=1, 698 | has_default_value=False, 699 | default_value=0, 700 | message_type=None, 701 | enum_type=None, 702 | containing_type=None, 703 | is_extension=False, 704 | extension_scope=None, 705 | options=None), 706 | _descriptor.FieldDescriptor( 707 | name='address', 708 | full_name='perftools.profiles.Location.address', 709 | index=2, 710 | number=3, 711 | type=4, 712 | cpp_type=4, 713 | label=1, 714 | has_default_value=False, 715 | default_value=0, 716 | message_type=None, 717 | enum_type=None, 718 | containing_type=None, 719 | is_extension=False, 720 | extension_scope=None, 721 | options=None), 722 | _descriptor.FieldDescriptor( 723 | name='line', 724 | full_name='perftools.profiles.Location.line', 725 | index=3, 726 | number=4, 727 | type=11, 728 | cpp_type=10, 729 | label=3, 730 | has_default_value=False, 731 | default_value=[], 732 | message_type=None, 733 | enum_type=None, 734 | containing_type=None, 735 | is_extension=False, 736 | extension_scope=None, 737 | options=None), 738 | _descriptor.FieldDescriptor( 739 | name='is_folded', 740 | full_name='perftools.profiles.Location.is_folded', 741 | index=4, 742 | number=5, 743 | type=8, 744 | cpp_type=7, 745 | label=1, 746 | has_default_value=False, 747 | default_value=False, 748 | message_type=None, 749 | enum_type=None, 750 | containing_type=None, 751 | is_extension=False, 752 | extension_scope=None, 753 | options=None), 754 | ], 755 | extensions=[], 756 | nested_types=[], 757 | enum_types=[], 758 | options=None, 759 | is_extendable=False, 760 | syntax='proto3', 761 | extension_ranges=[], 762 | oneofs=[], 763 | serialized_start=928, 764 | serialized_end=1046, 765 | ) 766 | 767 | _LINE = _descriptor.Descriptor( 768 | name='Line', 769 | full_name='perftools.profiles.Line', 770 | filename=None, 771 | file=DESCRIPTOR, 772 | containing_type=None, 773 | fields=[ 774 | _descriptor.FieldDescriptor( 775 | name='function_id', 776 | full_name='perftools.profiles.Line.function_id', 777 | index=0, 778 | number=1, 779 | type=4, 780 | cpp_type=4, 781 | label=1, 782 | has_default_value=False, 783 | default_value=0, 784 | message_type=None, 785 | enum_type=None, 786 | containing_type=None, 787 | is_extension=False, 788 | extension_scope=None, 789 | options=None), 790 | _descriptor.FieldDescriptor( 791 | name='line', 792 | full_name='perftools.profiles.Line.line', 793 | index=1, 794 | number=2, 795 | type=3, 796 | cpp_type=2, 797 | label=1, 798 | has_default_value=False, 799 | default_value=0, 800 | message_type=None, 801 | enum_type=None, 802 | containing_type=None, 803 | is_extension=False, 804 | extension_scope=None, 805 | options=None), 806 | ], 807 | extensions=[], 808 | nested_types=[], 809 | enum_types=[], 810 | options=None, 811 | is_extendable=False, 812 | syntax='proto3', 813 | extension_ranges=[], 814 | oneofs=[], 815 | serialized_start=1048, 816 | serialized_end=1089, 817 | ) 818 | 819 | _FUNCTION = _descriptor.Descriptor( 820 | name='Function', 821 | full_name='perftools.profiles.Function', 822 | filename=None, 823 | file=DESCRIPTOR, 824 | containing_type=None, 825 | fields=[ 826 | _descriptor.FieldDescriptor( 827 | name='id', 828 | full_name='perftools.profiles.Function.id', 829 | index=0, 830 | number=1, 831 | type=4, 832 | cpp_type=4, 833 | label=1, 834 | has_default_value=False, 835 | default_value=0, 836 | message_type=None, 837 | enum_type=None, 838 | containing_type=None, 839 | is_extension=False, 840 | extension_scope=None, 841 | options=None), 842 | _descriptor.FieldDescriptor( 843 | name='name', 844 | full_name='perftools.profiles.Function.name', 845 | index=1, 846 | number=2, 847 | type=3, 848 | cpp_type=2, 849 | label=1, 850 | has_default_value=False, 851 | default_value=0, 852 | message_type=None, 853 | enum_type=None, 854 | containing_type=None, 855 | is_extension=False, 856 | extension_scope=None, 857 | options=None), 858 | _descriptor.FieldDescriptor( 859 | name='system_name', 860 | full_name='perftools.profiles.Function.system_name', 861 | index=2, 862 | number=3, 863 | type=3, 864 | cpp_type=2, 865 | label=1, 866 | has_default_value=False, 867 | default_value=0, 868 | message_type=None, 869 | enum_type=None, 870 | containing_type=None, 871 | is_extension=False, 872 | extension_scope=None, 873 | options=None), 874 | _descriptor.FieldDescriptor( 875 | name='filename', 876 | full_name='perftools.profiles.Function.filename', 877 | index=3, 878 | number=4, 879 | type=3, 880 | cpp_type=2, 881 | label=1, 882 | has_default_value=False, 883 | default_value=0, 884 | message_type=None, 885 | enum_type=None, 886 | containing_type=None, 887 | is_extension=False, 888 | extension_scope=None, 889 | options=None), 890 | _descriptor.FieldDescriptor( 891 | name='start_line', 892 | full_name='perftools.profiles.Function.start_line', 893 | index=4, 894 | number=5, 895 | type=3, 896 | cpp_type=2, 897 | label=1, 898 | has_default_value=False, 899 | default_value=0, 900 | message_type=None, 901 | enum_type=None, 902 | containing_type=None, 903 | is_extension=False, 904 | extension_scope=None, 905 | options=None), 906 | ], 907 | extensions=[], 908 | nested_types=[], 909 | enum_types=[], 910 | options=None, 911 | is_extendable=False, 912 | syntax='proto3', 913 | extension_ranges=[], 914 | oneofs=[], 915 | serialized_start=1091, 916 | serialized_end=1186, 917 | ) 918 | 919 | _PROFILE.fields_by_name['sample_type'].message_type = _VALUETYPE 920 | _PROFILE.fields_by_name['sample'].message_type = _SAMPLE 921 | _PROFILE.fields_by_name['mapping'].message_type = _MAPPING 922 | _PROFILE.fields_by_name['location'].message_type = _LOCATION 923 | _PROFILE.fields_by_name['function'].message_type = _FUNCTION 924 | _PROFILE.fields_by_name['period_type'].message_type = _VALUETYPE 925 | _SAMPLE.fields_by_name['label'].message_type = _LABEL 926 | _LOCATION.fields_by_name['line'].message_type = _LINE 927 | DESCRIPTOR.message_types_by_name['Profile'] = _PROFILE 928 | DESCRIPTOR.message_types_by_name['ValueType'] = _VALUETYPE 929 | DESCRIPTOR.message_types_by_name['Sample'] = _SAMPLE 930 | DESCRIPTOR.message_types_by_name['Label'] = _LABEL 931 | DESCRIPTOR.message_types_by_name['Mapping'] = _MAPPING 932 | DESCRIPTOR.message_types_by_name['Location'] = _LOCATION 933 | DESCRIPTOR.message_types_by_name['Line'] = _LINE 934 | DESCRIPTOR.message_types_by_name['Function'] = _FUNCTION 935 | 936 | Profile = _reflection.GeneratedProtocolMessageType( 937 | 'Profile', 938 | (_message.Message,), 939 | dict( 940 | DESCRIPTOR=_PROFILE, 941 | __module__='profile_pb2' 942 | # @@protoc_insertion_point(class_scope:perftools.profiles.Profile) 943 | )) 944 | _sym_db.RegisterMessage(Profile) 945 | 946 | ValueType = _reflection.GeneratedProtocolMessageType( 947 | 'ValueType', 948 | (_message.Message,), 949 | dict( 950 | DESCRIPTOR=_VALUETYPE, 951 | __module__='profile_pb2' 952 | # @@protoc_insertion_point(class_scope:perftools.profiles.ValueType) 953 | )) 954 | _sym_db.RegisterMessage(ValueType) 955 | 956 | Sample = _reflection.GeneratedProtocolMessageType( 957 | 'Sample', 958 | (_message.Message,), 959 | dict( 960 | DESCRIPTOR=_SAMPLE, 961 | __module__='profile_pb2' 962 | # @@protoc_insertion_point(class_scope:perftools.profiles.Sample) 963 | )) 964 | _sym_db.RegisterMessage(Sample) 965 | 966 | Label = _reflection.GeneratedProtocolMessageType( 967 | 'Label', 968 | (_message.Message,), 969 | dict( 970 | DESCRIPTOR=_LABEL, 971 | __module__='profile_pb2' 972 | # @@protoc_insertion_point(class_scope:perftools.profiles.Label) 973 | )) 974 | _sym_db.RegisterMessage(Label) 975 | 976 | Mapping = _reflection.GeneratedProtocolMessageType( 977 | 'Mapping', 978 | (_message.Message,), 979 | dict( 980 | DESCRIPTOR=_MAPPING, 981 | __module__='profile_pb2' 982 | # @@protoc_insertion_point(class_scope:perftools.profiles.Mapping) 983 | )) 984 | _sym_db.RegisterMessage(Mapping) 985 | 986 | Location = _reflection.GeneratedProtocolMessageType( 987 | 'Location', 988 | (_message.Message,), 989 | dict( 990 | DESCRIPTOR=_LOCATION, 991 | __module__='profile_pb2' 992 | # @@protoc_insertion_point(class_scope:perftools.profiles.Location) 993 | )) 994 | _sym_db.RegisterMessage(Location) 995 | 996 | Line = _reflection.GeneratedProtocolMessageType( 997 | 'Line', 998 | (_message.Message,), 999 | dict( 1000 | DESCRIPTOR=_LINE, 1001 | __module__='profile_pb2' 1002 | # @@protoc_insertion_point(class_scope:perftools.profiles.Line) 1003 | )) 1004 | _sym_db.RegisterMessage(Line) 1005 | 1006 | Function = _reflection.GeneratedProtocolMessageType( 1007 | 'Function', 1008 | (_message.Message,), 1009 | dict( 1010 | DESCRIPTOR=_FUNCTION, 1011 | __module__='profile_pb2' 1012 | # @@protoc_insertion_point(class_scope:perftools.profiles.Function) 1013 | )) 1014 | _sym_db.RegisterMessage(Function) 1015 | 1016 | DESCRIPTOR.has_options = True 1017 | DESCRIPTOR._options = _descriptor._ParseOptions( 1018 | descriptor_pb2.FileOptions(), 1019 | _b('\n\035com.google.perftools.profilesB\014ProfileProto')) 1020 | # @@protoc_insertion_point(module_scope) 1021 | --------------------------------------------------------------------------------