gdb2dict 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2016 The Python Packaging Authority (PyPA)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
@@ -0,0 +1,212 @@
1
+ Metadata-Version: 2.1
2
+ Name: gdb2dict
3
+ Version: 1.0.0
4
+ Summary: A converter from GDB values to python dict
5
+ Author-email: Zakaria FADLI <zakaria1193@gmail.com>
6
+ Maintainer-email: Zakaria FADLI <zakaria1193@gmail.com>
7
+ License: Copyright (c) 2016 The Python Packaging Authority (PyPA)
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
10
+ this software and associated documentation files (the "Software"), to deal in
11
+ the Software without restriction, including without limitation the rights to
12
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
13
+ of the Software, and to permit persons to whom the Software is furnished to do
14
+ so, subject to the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in all
17
+ copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ SOFTWARE.
26
+
27
+ Project-URL: Homepage, https://github.com/zakaria1193/gdb2dict
28
+ Project-URL: Bug Reports, https://github.com/zakaria1193/gdb2dict/issues
29
+ Project-URL: Funding, https://donate.pypi.org
30
+ Project-URL: Say Thanks!, http://saythanks.io/to/example
31
+ Project-URL: Source, https://github.com/zakaria1193/gdb2dict
32
+ Classifier: Development Status :: 3 - Alpha
33
+ Classifier: Intended Audience :: Developers
34
+ Classifier: Topic :: Software Development :: Build Tools
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Programming Language :: Python :: 3
37
+ Classifier: Programming Language :: Python :: 3.7
38
+ Classifier: Programming Language :: Python :: 3.8
39
+ Classifier: Programming Language :: Python :: 3.9
40
+ Classifier: Programming Language :: Python :: 3.10
41
+ Classifier: Programming Language :: Python :: 3.11
42
+ Classifier: Programming Language :: Python :: 3 :: Only
43
+ Requires-Python: >=3.7
44
+ Description-Content-Type: text/markdown
45
+ License-File: LICENSE.txt
46
+ Provides-Extra: dev
47
+ Requires-Dist: check-manifest; extra == "dev"
48
+ Provides-Extra: test
49
+ Requires-Dist: coverage; extra == "test"
50
+
51
+ # GDB.value to python dict converter
52
+
53
+ This tool extends GDB python scripting capabilities.
54
+
55
+ gdb (GNU Debugger) use a specific format to print C/C++ programs data, this format is not easy to parse, so this tool converts the output of gdb to python dictionaries.
56
+
57
+ So it can also be used to serialize C data structures to JSON objects, or to any other format.
58
+
59
+ Example of conversion:
60
+
61
+ If you have a structure like this in you C code:
62
+
63
+ ```c
64
+ struct Shape {
65
+ int id;
66
+ enum {
67
+ RED,
68
+ GREEN,
69
+ BLUE
70
+ } color;
71
+ union {
72
+ int intValue;
73
+ float floatValue;
74
+ }; // unnamed union (C11)
75
+ struct {
76
+ int x;
77
+ int y;
78
+ } center;
79
+ union {
80
+ int intValue;
81
+ float floatValue;
82
+ } data;
83
+ };
84
+ ```
85
+
86
+ When you print an instance of this struct in python gdb script (or in the classic gdb console),
87
+ both will give this printable string that is not a native python object
88
+
89
+ ```gdb
90
+ (gdb) p my_struct
91
+ OR
92
+ (gdb) python print(gdb.parse_and_eval("my_struct"))
93
+ {
94
+ id = 0x1,
95
+ color = RED,
96
+ {
97
+ intValue = 0x2a,
98
+ floatValue = 5.88545355e-44
99
+ },
100
+ center = {
101
+ x = 0x1e,
102
+ y = 0x28
103
+ },
104
+ data = {
105
+ intValue = 0x6c6c6548,
106
+ floatValue = 1.14313912e+27,
107
+ }
108
+ }
109
+ ```
110
+
111
+ gdb2dict lets convert the output of gdb to a python dictionary.
112
+
113
+ ```python
114
+ import gdb2dict
115
+ ```
116
+
117
+ Simply call the function `gdb_value_to_dict` with the value to convert,
118
+ and it will return a python dictionary.
119
+
120
+ ```python
121
+ > output_dict = gdb2dict.gdb_value_to_dict(gdb.parse_and_eval("my_struct"))
122
+
123
+ output_dict =
124
+ {
125
+ 'id': '0x1',
126
+ 'color': 'RED',
127
+ '::unnamed_field_1::union':
128
+ {
129
+ 'floatValue': '5.88545355e-44',
130
+ 'intValue': '0x2a'
131
+ },
132
+ 'center::struct': {'x': '0x1e', 'y': '0x28'},
133
+ 'data::union':
134
+ {
135
+ 'floatValue': '1.14313912e+27',
136
+ 'intValue': '0x6c6c6548'
137
+ },
138
+ }
139
+
140
+ ```
141
+
142
+ ### Metatada
143
+
144
+ As you can see some field names (keys after conversion) have added metadata **::struct**, **::union**
145
+ That's needed to differentiate between fields that are structs and fields that are unions.
146
+
147
+ Another metadata can be added to the keys, it's **::unnamed_field_1::struct**,
148
+ **::unnamed_field_2::union** etc...
149
+
150
+ That's to cover for [ C11's unnanmed fields ](https://gcc.gnu.org/onlinedocs/gcc/Unnamed-Fields.html)
151
+ that can be sub-structs or sub-unions without a name.
152
+
153
+ ## Use cases
154
+
155
+ Imagine you are trying to automatize the debugging of a measuring, and you want to parse the output of a measure function that returns a structure, you can use this tool to convert the output of gdb to a JSON format,
156
+ you can do that manually by reading field by and field and making your own python dictionary, but this tool does that for you.
157
+
158
+ It comes handy when you have a lot of structures and unions to parse, and you don't want to write a lot of code to parse them, since gdb already knows how to parse them.
159
+
160
+ The printer can be used in your custom breakpoints, or your custom commands, or in your custom pretty printers.
161
+
162
+ If you don't know how to make those, refer to the [GDB documentation](https://sourceware.org/gdb/onlinedocs/gdb/Python-API.html#Python-API).
163
+
164
+ Or this article from [Memfault](https://interrupt.memfault.com/blog/automate-debugging-with-gdb-python-api)
165
+
166
+ ## Usage example: Parse TLV data from breakpoint and write to file
167
+
168
+ Let's use it in a python scripted gdb breakpoint handler.
169
+ The idea is catch the functions that identifies the TLV type and value,
170
+ then cast to a structure and write to a file in JSON format.
171
+
172
+ ```python my_script.py
173
+
174
+ import gdb2dict
175
+
176
+ OUTPUT_LIST = []
177
+
178
+ class MyCustomBreakpoint(gdb.Breakpoint):
179
+ def stop(self):
180
+ # Access arguments
181
+ arg1_payload = gdb.parse_and_eval("arg1_payload")
182
+ arg2_payload_type = gdb.parse_and_eval("arg2_paytload_type")
183
+ arg3_payload_size = gdb.parse_and_eval("arg3_payload_size") # Not needed here
184
+
185
+ # Convert the payload type to a structure using some custom mapping function
186
+ type_to_cast = my_custom_payload_type_to_struct(arg2_payload_type)
187
+
188
+ # Cast to a structure pointer
189
+ arg1_payload = arg1_payload.cast(type_to_cast)
190
+
191
+ OUTPUT_LIST.append(gdb2dict.gdb_value_to_dict(arg1_payload))
192
+
193
+ # Return False to not halt (Automatically continue)
194
+ return False
195
+
196
+ with open("output.json", "w") as f:
197
+ f.write("{\"output\": [\n")
198
+ f.write(",\n".join(OUTPUT_LIST))
199
+ f.write("]}")
200
+
201
+ ```
202
+
203
+ Source this script in gdb will set the breakpoint and fill the output file with the parsed values.
204
+
205
+ ```bash
206
+ $ gdb -x my_script.py --batch --nw --nx --return-child-result
207
+ ```
208
+
209
+ `--batch --nw --nx --return-child-result` are recommended for automated gdb scripting,
210
+ see `gdb --help` for more information.
211
+
212
+ Then you can post process the output file with your favorite language.
@@ -0,0 +1,162 @@
1
+ # GDB.value to python dict converter
2
+
3
+ This tool extends GDB python scripting capabilities.
4
+
5
+ gdb (GNU Debugger) use a specific format to print C/C++ programs data, this format is not easy to parse, so this tool converts the output of gdb to python dictionaries.
6
+
7
+ So it can also be used to serialize C data structures to JSON objects, or to any other format.
8
+
9
+ Example of conversion:
10
+
11
+ If you have a structure like this in you C code:
12
+
13
+ ```c
14
+ struct Shape {
15
+ int id;
16
+ enum {
17
+ RED,
18
+ GREEN,
19
+ BLUE
20
+ } color;
21
+ union {
22
+ int intValue;
23
+ float floatValue;
24
+ }; // unnamed union (C11)
25
+ struct {
26
+ int x;
27
+ int y;
28
+ } center;
29
+ union {
30
+ int intValue;
31
+ float floatValue;
32
+ } data;
33
+ };
34
+ ```
35
+
36
+ When you print an instance of this struct in python gdb script (or in the classic gdb console),
37
+ both will give this printable string that is not a native python object
38
+
39
+ ```gdb
40
+ (gdb) p my_struct
41
+ OR
42
+ (gdb) python print(gdb.parse_and_eval("my_struct"))
43
+ {
44
+ id = 0x1,
45
+ color = RED,
46
+ {
47
+ intValue = 0x2a,
48
+ floatValue = 5.88545355e-44
49
+ },
50
+ center = {
51
+ x = 0x1e,
52
+ y = 0x28
53
+ },
54
+ data = {
55
+ intValue = 0x6c6c6548,
56
+ floatValue = 1.14313912e+27,
57
+ }
58
+ }
59
+ ```
60
+
61
+ gdb2dict lets convert the output of gdb to a python dictionary.
62
+
63
+ ```python
64
+ import gdb2dict
65
+ ```
66
+
67
+ Simply call the function `gdb_value_to_dict` with the value to convert,
68
+ and it will return a python dictionary.
69
+
70
+ ```python
71
+ > output_dict = gdb2dict.gdb_value_to_dict(gdb.parse_and_eval("my_struct"))
72
+
73
+ output_dict =
74
+ {
75
+ 'id': '0x1',
76
+ 'color': 'RED',
77
+ '::unnamed_field_1::union':
78
+ {
79
+ 'floatValue': '5.88545355e-44',
80
+ 'intValue': '0x2a'
81
+ },
82
+ 'center::struct': {'x': '0x1e', 'y': '0x28'},
83
+ 'data::union':
84
+ {
85
+ 'floatValue': '1.14313912e+27',
86
+ 'intValue': '0x6c6c6548'
87
+ },
88
+ }
89
+
90
+ ```
91
+
92
+ ### Metatada
93
+
94
+ As you can see some field names (keys after conversion) have added metadata **::struct**, **::union**
95
+ That's needed to differentiate between fields that are structs and fields that are unions.
96
+
97
+ Another metadata can be added to the keys, it's **::unnamed_field_1::struct**,
98
+ **::unnamed_field_2::union** etc...
99
+
100
+ That's to cover for [ C11's unnanmed fields ](https://gcc.gnu.org/onlinedocs/gcc/Unnamed-Fields.html)
101
+ that can be sub-structs or sub-unions without a name.
102
+
103
+ ## Use cases
104
+
105
+ Imagine you are trying to automatize the debugging of a measuring, and you want to parse the output of a measure function that returns a structure, you can use this tool to convert the output of gdb to a JSON format,
106
+ you can do that manually by reading field by and field and making your own python dictionary, but this tool does that for you.
107
+
108
+ It comes handy when you have a lot of structures and unions to parse, and you don't want to write a lot of code to parse them, since gdb already knows how to parse them.
109
+
110
+ The printer can be used in your custom breakpoints, or your custom commands, or in your custom pretty printers.
111
+
112
+ If you don't know how to make those, refer to the [GDB documentation](https://sourceware.org/gdb/onlinedocs/gdb/Python-API.html#Python-API).
113
+
114
+ Or this article from [Memfault](https://interrupt.memfault.com/blog/automate-debugging-with-gdb-python-api)
115
+
116
+ ## Usage example: Parse TLV data from breakpoint and write to file
117
+
118
+ Let's use it in a python scripted gdb breakpoint handler.
119
+ The idea is catch the functions that identifies the TLV type and value,
120
+ then cast to a structure and write to a file in JSON format.
121
+
122
+ ```python my_script.py
123
+
124
+ import gdb2dict
125
+
126
+ OUTPUT_LIST = []
127
+
128
+ class MyCustomBreakpoint(gdb.Breakpoint):
129
+ def stop(self):
130
+ # Access arguments
131
+ arg1_payload = gdb.parse_and_eval("arg1_payload")
132
+ arg2_payload_type = gdb.parse_and_eval("arg2_paytload_type")
133
+ arg3_payload_size = gdb.parse_and_eval("arg3_payload_size") # Not needed here
134
+
135
+ # Convert the payload type to a structure using some custom mapping function
136
+ type_to_cast = my_custom_payload_type_to_struct(arg2_payload_type)
137
+
138
+ # Cast to a structure pointer
139
+ arg1_payload = arg1_payload.cast(type_to_cast)
140
+
141
+ OUTPUT_LIST.append(gdb2dict.gdb_value_to_dict(arg1_payload))
142
+
143
+ # Return False to not halt (Automatically continue)
144
+ return False
145
+
146
+ with open("output.json", "w") as f:
147
+ f.write("{\"output\": [\n")
148
+ f.write(",\n".join(OUTPUT_LIST))
149
+ f.write("]}")
150
+
151
+ ```
152
+
153
+ Source this script in gdb will set the breakpoint and fill the output file with the parsed values.
154
+
155
+ ```bash
156
+ $ gdb -x my_script.py --batch --nw --nx --return-child-result
157
+ ```
158
+
159
+ `--batch --nw --nx --return-child-result` are recommended for automated gdb scripting,
160
+ see `gdb --help` for more information.
161
+
162
+ Then you can post process the output file with your favorite language.
@@ -0,0 +1,152 @@
1
+ [project]
2
+ # This is the name of your project. The first time you publish this
3
+ # package, this name will be registered for you. It will determine how
4
+ # users can install this project, e.g.:
5
+ #
6
+ # $ pip install sampleproject
7
+ #
8
+ # And where it will live on PyPI: https://pypi.org/project/sampleproject/
9
+ #
10
+ # There are some restrictions on what makes a valid project name
11
+ # specification here:
12
+ # https://packaging.python.org/specifications/core-metadata/#name
13
+ name = "gdb2dict" # Required
14
+
15
+ # Versions should comply with PEP 440:
16
+ # https://www.python.org/dev/peps/pep-0440/
17
+ #
18
+ # For a discussion on single-sourcing the version, see
19
+ # https://packaging.python.org/guides/single-sourcing-package-version/
20
+ version = "1.0.0" # Required
21
+
22
+ # This is a one-line description or tagline of what your project does. This
23
+ # corresponds to the "Summary" metadata field:
24
+ # https://packaging.python.org/specifications/core-metadata/#summary
25
+ description = "A converter from GDB values to python dict" # Required
26
+
27
+ # This is an optional longer description of your project that represents
28
+ # the body of text which users will see when they visit PyPI.
29
+ #
30
+ # Often, this is the same as your README, so you can just read it in from
31
+ # that file directly (as we have already done above)
32
+ #
33
+ # This field corresponds to the "Description" metadata field:
34
+ # https://packaging.python.org/specifications/core-metadata/#description-optional
35
+ readme = "README.md" # Optional
36
+
37
+ # Specify which Python versions you support. In contrast to the
38
+ # 'Programming Language' classifiers above, 'pip install' will check this
39
+ # and refuse to install the project if the version does not match. See
40
+ # https://packaging.python.org/guides/distributing-packages-using-setuptools/#python-requires
41
+ requires-python = ">=3.7"
42
+
43
+ # This is either text indicating the license for the distribution, or a file
44
+ # that contains the license
45
+ # https://packaging.python.org/en/latest/specifications/core-metadata/#license
46
+ license = {file = "LICENSE.txt"}
47
+
48
+ # This field adds keywords for your project which will appear on the
49
+ # project page. What does your project relate to?
50
+ #
51
+ # Note that this is a list of additional keywords, separated
52
+ # by commas, to be used to assist searching for the distribution in a
53
+ # larger catalog.
54
+ keywords = [] # Optional
55
+
56
+ # This should be your name or the name of the organization who originally
57
+ # authored the project, and a valid email address corresponding to the name
58
+ # listed.
59
+ authors = [
60
+ {name = "Zakaria FADLI", email = "zakaria1193@gmail.com" } # Optional
61
+ ]
62
+
63
+ # This should be your name or the names of the organization who currently
64
+ # maintains the project, and a valid email address corresponding to the name
65
+ # listed.
66
+ maintainers = [
67
+ {name = "Zakaria FADLI", email = "zakaria1193@gmail.com" } # Optional
68
+ ]
69
+
70
+ # Classifiers help users find your project by categorizing it.
71
+ #
72
+ # For a list of valid classifiers, see https://pypi.org/classifiers/
73
+ classifiers = [ # Optional
74
+ # How mature is this project? Common values are
75
+ # 3 - Alpha
76
+ # 4 - Beta
77
+ # 5 - Production/Stable
78
+ "Development Status :: 3 - Alpha",
79
+
80
+ # Indicate who your project is intended for
81
+ "Intended Audience :: Developers",
82
+ "Topic :: Software Development :: Build Tools",
83
+
84
+ # Pick your license as you wish
85
+ "License :: OSI Approved :: MIT License",
86
+
87
+ # Specify the Python versions you support here. In particular, ensure
88
+ # that you indicate you support Python 3. These classifiers are *not*
89
+ # checked by "pip install". See instead "python_requires" below.
90
+ "Programming Language :: Python :: 3",
91
+ "Programming Language :: Python :: 3.7",
92
+ "Programming Language :: Python :: 3.8",
93
+ "Programming Language :: Python :: 3.9",
94
+ "Programming Language :: Python :: 3.10",
95
+ "Programming Language :: Python :: 3.11",
96
+ "Programming Language :: Python :: 3 :: Only",
97
+ ]
98
+
99
+ # This field lists other packages that your project depends on to run.
100
+ # Any package you put here will be installed by pip when your project is
101
+ # installed, so they must be valid existing projects.
102
+ #
103
+ # For an analysis of this field vs pip's requirements files see:
104
+ # https://packaging.python.org/discussions/install-requires-vs-requirements/
105
+ dependencies = [ # Optional
106
+ ]
107
+
108
+ # List additional groups of dependencies here (e.g. development
109
+ # dependencies). Users will be able to install these using the "extras"
110
+ # syntax, for example:
111
+ #
112
+ # $ pip install sampleproject[dev]
113
+ #
114
+ # Similar to `dependencies` above, these must be valid existing
115
+ # projects.
116
+ [project.optional-dependencies] # Optional
117
+ dev = ["check-manifest"]
118
+ test = ["coverage"]
119
+
120
+ # List URLs that are relevant to your project
121
+ #
122
+ # This field corresponds to the "Project-URL" and "Home-Page" metadata fields:
123
+ # https://packaging.python.org/specifications/core-metadata/#project-url-multiple-use
124
+ # https://packaging.python.org/specifications/core-metadata/#home-page-optional
125
+ #
126
+ # Examples listed include a pattern for specifying where the package tracks
127
+ # issues, where the source is hosted, where to say thanks to the package
128
+ # maintainers, and where to support the project financially. The key is
129
+ # what's used to render the link text on PyPI.
130
+ [project.urls] # Optional
131
+ "Homepage" = "https://github.com/zakaria1193/gdb2dict"
132
+ "Bug Reports" = "https://github.com/zakaria1193/gdb2dict/issues"
133
+ "Funding" = "https://donate.pypi.org"
134
+ "Say Thanks!" = "http://saythanks.io/to/example"
135
+ "Source" = "https://github.com/zakaria1193/gdb2dict"
136
+
137
+ # The following would provide a command line executable called `sample`
138
+ # which executes the function `main` from this package when invoked.
139
+ [project.scripts] # Optional
140
+
141
+ # This is configuration specific to the `setuptools` build backend.
142
+ # If you are using a different build backend, you will need to change this.
143
+ [tool.setuptools]
144
+ # If there are data files included in your packages that need to be
145
+ # installed, specify them here.
146
+ package-data = {}
147
+
148
+ [build-system]
149
+ # These are the assumed default build requirements from pip:
150
+ # https://pip.pypa.io/en/stable/reference/pip/#pep-517-and-518-support
151
+ requires = ["setuptools>=43.0.0", "wheel"]
152
+ build-backend = "setuptools.build_meta"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,6 @@
1
+ from .gdb_value_to_dict import gdb_value_to_dict
2
+
3
+ # Export all imported
4
+ __all__ = [
5
+ "gdb_value_to_dict",
6
+ ]
@@ -0,0 +1,171 @@
1
+ import json
2
+
3
+ import gdb
4
+
5
+ DEBUG = False
6
+
7
+ if DEBUG:
8
+ def print_debug(msg):
9
+ print(msg)
10
+ else:
11
+ def print_debug(msg):
12
+ pass
13
+
14
+
15
+ OBJ_TYPE_NEEDS_RECURSIVE_CALL = [
16
+ gdb.TYPE_CODE_STRUCT,
17
+ gdb.TYPE_CODE_UNION,
18
+ gdb.TYPE_CODE_ARRAY
19
+ ]
20
+
21
+
22
+ def gdb_value_to_dict(gdb_value: gdb.Value):
23
+ """
24
+ Converts a gdb.Value to a json string
25
+
26
+ :param gdb_value: gdb.Value -> Must be a struct or union
27
+ :return: data: dict -> The data of the gdb.Value as a dict
28
+ """
29
+
30
+ data: dict = {}
31
+ append_gdb_value_to_dict(gdb_value, data)
32
+ return data
33
+
34
+
35
+ def gdb_value_primitive_to_str(gdb_value: gdb.Value):
36
+ """
37
+ Converts a primitive gdb.Value to a str or hex
38
+
39
+ A primitive gdb.Value is a value that is not of the types listed in
40
+ OBJ_TYPE_NEEDS_RECURSIVE_CALL (struct, union, array, etc...)
41
+
42
+ :param gdb_value: gdb.Value to be converted
43
+ :return: str or hex of the gdb.Value
44
+ """
45
+
46
+ print_debug("gdb_value_primitive_to_str: {}".format(gdb_value))
47
+
48
+ gdb_value_type_code = gdb_value.type.strip_typedefs().code
49
+
50
+ if gdb_value_type_code in OBJ_TYPE_NEEDS_RECURSIVE_CALL:
51
+ raise Exception("obj_value_to_value called on a non primitive type")
52
+
53
+ if gdb_value_type_code == gdb.TYPE_CODE_INT:
54
+ return hex(int(gdb_value))
55
+
56
+ return str(gdb_value)
57
+
58
+
59
+ def append_gdb_value_to_dict(gdb_value: gdb.Value, data: dict):
60
+ """
61
+ Recursive function to convert gdb.Value object to dict key/values
62
+ and add them into given dict.
63
+
64
+ //!\\ Function is recursive
65
+
66
+ :param gdb_value: gdb.Value to be appended (Can be any C type)
67
+ :param data: dict to be filled
68
+ :return: None
69
+ """
70
+
71
+ print_debug("append_gdb_value_to_dict: {}".format(gdb_value))
72
+
73
+ assert isinstance(data, dict), f"data must be a dict, not {type(data)}"
74
+
75
+ gdb_value_type_code = gdb_value.type.strip_typedefs().code
76
+
77
+ print_debug("-- struct_type: {} code: {}".format(gdb_value.type,
78
+ gdb_value.type.code))
79
+
80
+ if gdb_value_type_code not in OBJ_TYPE_NEEDS_RECURSIVE_CALL:
81
+ raise Exception("obj_value_to_dict called on a primitive type")
82
+
83
+ # Recursively extract data from nested structs/unions/arrays
84
+ fields = gdb_value.type.fields()
85
+
86
+ for i, field in enumerate(fields):
87
+ field_name = field.name
88
+
89
+ if field_name is None:
90
+ # This happens with anonymous struct field (C11)
91
+ # https://gcc.gnu.org/onlinedocs/gcc/Unnamed-Fields.html
92
+ field_name = "::unnamed_field_{}".format(i)
93
+ # Field value should not be accessed by field_name
94
+ # This is documented in:
95
+ # https://sourceware.org/bugzilla/show_bug.cgi?id=15464
96
+
97
+ field_value = gdb_value[field]
98
+
99
+ field_value = gdb_value[field]
100
+
101
+ field_type = field_value.type.strip_typedefs()
102
+ field_type_code = field_type.code
103
+
104
+ print_debug("---- subfield_name: {} "
105
+ "type_raw: {} {} "
106
+ "type_: {} {} ".format(
107
+ field_name,
108
+ field.type,
109
+ field.type.code,
110
+ field_type, field_type.code))
111
+
112
+ if field_type_code in (gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_UNION):
113
+ print_debug("Creating struct under data [{}]".format(field_name))
114
+
115
+ # If the field is a nested struct, recursively extract its data
116
+ struct_data: dict = {}
117
+ append_gdb_value_to_dict(field_value, struct_data)
118
+ if field_type_code == gdb.TYPE_CODE_STRUCT:
119
+ key_ = field_name + "::struct"
120
+ else: # gdb.TYPE_CODE_UNION
121
+ key_ = field_name + "::union"
122
+ data[key_] = struct_data
123
+
124
+ elif field_type_code == gdb.TYPE_CODE_ARRAY:
125
+ print_debug("Creating array under data [{}]".format(field_name))
126
+ # Initialize it as a list
127
+ key_ = field_name + "::array"
128
+ data[key_] = []
129
+
130
+ # For each item in the array, recursively extract its data and
131
+ # append it to the list
132
+ for j in range(field_type.range()[1]):
133
+ append_gdb_value_to_list(field_value[j], data[key_])
134
+
135
+ else:
136
+ print_debug("Creating primitive under [{}]".format(field_name))
137
+ data[field_name] = gdb_value_primitive_to_str(field_value)
138
+
139
+
140
+ def append_gdb_value_to_list(gdb_value: gdb.Value,
141
+ list_to_fill: list):
142
+ """
143
+ Recursive function to convert gdb.Value object to dict key/values and adds
144
+ them into given dict
145
+
146
+ //!\\ Function is recursive (indirectly through append_gdb_value_to_dict)
147
+
148
+ :param gdb_value: gdb.Value to be appended (Can be any C type)
149
+ :param data: dict to be filled
150
+ :return: None
151
+ """
152
+
153
+ print_debug("append_gdb_value_to_list: {}".format(gdb_value))
154
+
155
+ assert isinstance(list_to_fill, list), "list_to_fill must be a list, "\
156
+ f"not {type(list_to_fill)}"
157
+
158
+ obj_value_code = gdb_value.type.strip_typedefs().code
159
+ # If the object is a struct, union, array...
160
+ # put it in a dict then append it
161
+ if obj_value_code in OBJ_TYPE_NEEDS_RECURSIVE_CALL:
162
+ item: dict = {}
163
+ append_gdb_value_to_dict(gdb_value, item)
164
+ list_to_fill.append(item)
165
+
166
+ # Primitive type (int, char, enum, etc) append it directly
167
+ else:
168
+ list_to_fill.append(gdb_value_primitive_to_str(gdb_value))
169
+
170
+ print_debug("Returning data: {}".format(json.dumps(list_to_fill,
171
+ indent=4)))
@@ -0,0 +1,212 @@
1
+ Metadata-Version: 2.1
2
+ Name: gdb2dict
3
+ Version: 1.0.0
4
+ Summary: A converter from GDB values to python dict
5
+ Author-email: Zakaria FADLI <zakaria1193@gmail.com>
6
+ Maintainer-email: Zakaria FADLI <zakaria1193@gmail.com>
7
+ License: Copyright (c) 2016 The Python Packaging Authority (PyPA)
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
10
+ this software and associated documentation files (the "Software"), to deal in
11
+ the Software without restriction, including without limitation the rights to
12
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
13
+ of the Software, and to permit persons to whom the Software is furnished to do
14
+ so, subject to the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in all
17
+ copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ SOFTWARE.
26
+
27
+ Project-URL: Homepage, https://github.com/zakaria1193/gdb2dict
28
+ Project-URL: Bug Reports, https://github.com/zakaria1193/gdb2dict/issues
29
+ Project-URL: Funding, https://donate.pypi.org
30
+ Project-URL: Say Thanks!, http://saythanks.io/to/example
31
+ Project-URL: Source, https://github.com/zakaria1193/gdb2dict
32
+ Classifier: Development Status :: 3 - Alpha
33
+ Classifier: Intended Audience :: Developers
34
+ Classifier: Topic :: Software Development :: Build Tools
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Programming Language :: Python :: 3
37
+ Classifier: Programming Language :: Python :: 3.7
38
+ Classifier: Programming Language :: Python :: 3.8
39
+ Classifier: Programming Language :: Python :: 3.9
40
+ Classifier: Programming Language :: Python :: 3.10
41
+ Classifier: Programming Language :: Python :: 3.11
42
+ Classifier: Programming Language :: Python :: 3 :: Only
43
+ Requires-Python: >=3.7
44
+ Description-Content-Type: text/markdown
45
+ License-File: LICENSE.txt
46
+ Provides-Extra: dev
47
+ Requires-Dist: check-manifest; extra == "dev"
48
+ Provides-Extra: test
49
+ Requires-Dist: coverage; extra == "test"
50
+
51
+ # GDB.value to python dict converter
52
+
53
+ This tool extends GDB python scripting capabilities.
54
+
55
+ gdb (GNU Debugger) use a specific format to print C/C++ programs data, this format is not easy to parse, so this tool converts the output of gdb to python dictionaries.
56
+
57
+ So it can also be used to serialize C data structures to JSON objects, or to any other format.
58
+
59
+ Example of conversion:
60
+
61
+ If you have a structure like this in you C code:
62
+
63
+ ```c
64
+ struct Shape {
65
+ int id;
66
+ enum {
67
+ RED,
68
+ GREEN,
69
+ BLUE
70
+ } color;
71
+ union {
72
+ int intValue;
73
+ float floatValue;
74
+ }; // unnamed union (C11)
75
+ struct {
76
+ int x;
77
+ int y;
78
+ } center;
79
+ union {
80
+ int intValue;
81
+ float floatValue;
82
+ } data;
83
+ };
84
+ ```
85
+
86
+ When you print an instance of this struct in python gdb script (or in the classic gdb console),
87
+ both will give this printable string that is not a native python object
88
+
89
+ ```gdb
90
+ (gdb) p my_struct
91
+ OR
92
+ (gdb) python print(gdb.parse_and_eval("my_struct"))
93
+ {
94
+ id = 0x1,
95
+ color = RED,
96
+ {
97
+ intValue = 0x2a,
98
+ floatValue = 5.88545355e-44
99
+ },
100
+ center = {
101
+ x = 0x1e,
102
+ y = 0x28
103
+ },
104
+ data = {
105
+ intValue = 0x6c6c6548,
106
+ floatValue = 1.14313912e+27,
107
+ }
108
+ }
109
+ ```
110
+
111
+ gdb2dict lets convert the output of gdb to a python dictionary.
112
+
113
+ ```python
114
+ import gdb2dict
115
+ ```
116
+
117
+ Simply call the function `gdb_value_to_dict` with the value to convert,
118
+ and it will return a python dictionary.
119
+
120
+ ```python
121
+ > output_dict = gdb2dict.gdb_value_to_dict(gdb.parse_and_eval("my_struct"))
122
+
123
+ output_dict =
124
+ {
125
+ 'id': '0x1',
126
+ 'color': 'RED',
127
+ '::unnamed_field_1::union':
128
+ {
129
+ 'floatValue': '5.88545355e-44',
130
+ 'intValue': '0x2a'
131
+ },
132
+ 'center::struct': {'x': '0x1e', 'y': '0x28'},
133
+ 'data::union':
134
+ {
135
+ 'floatValue': '1.14313912e+27',
136
+ 'intValue': '0x6c6c6548'
137
+ },
138
+ }
139
+
140
+ ```
141
+
142
+ ### Metatada
143
+
144
+ As you can see some field names (keys after conversion) have added metadata **::struct**, **::union**
145
+ That's needed to differentiate between fields that are structs and fields that are unions.
146
+
147
+ Another metadata can be added to the keys, it's **::unnamed_field_1::struct**,
148
+ **::unnamed_field_2::union** etc...
149
+
150
+ That's to cover for [ C11's unnanmed fields ](https://gcc.gnu.org/onlinedocs/gcc/Unnamed-Fields.html)
151
+ that can be sub-structs or sub-unions without a name.
152
+
153
+ ## Use cases
154
+
155
+ Imagine you are trying to automatize the debugging of a measuring, and you want to parse the output of a measure function that returns a structure, you can use this tool to convert the output of gdb to a JSON format,
156
+ you can do that manually by reading field by and field and making your own python dictionary, but this tool does that for you.
157
+
158
+ It comes handy when you have a lot of structures and unions to parse, and you don't want to write a lot of code to parse them, since gdb already knows how to parse them.
159
+
160
+ The printer can be used in your custom breakpoints, or your custom commands, or in your custom pretty printers.
161
+
162
+ If you don't know how to make those, refer to the [GDB documentation](https://sourceware.org/gdb/onlinedocs/gdb/Python-API.html#Python-API).
163
+
164
+ Or this article from [Memfault](https://interrupt.memfault.com/blog/automate-debugging-with-gdb-python-api)
165
+
166
+ ## Usage example: Parse TLV data from breakpoint and write to file
167
+
168
+ Let's use it in a python scripted gdb breakpoint handler.
169
+ The idea is catch the functions that identifies the TLV type and value,
170
+ then cast to a structure and write to a file in JSON format.
171
+
172
+ ```python my_script.py
173
+
174
+ import gdb2dict
175
+
176
+ OUTPUT_LIST = []
177
+
178
+ class MyCustomBreakpoint(gdb.Breakpoint):
179
+ def stop(self):
180
+ # Access arguments
181
+ arg1_payload = gdb.parse_and_eval("arg1_payload")
182
+ arg2_payload_type = gdb.parse_and_eval("arg2_paytload_type")
183
+ arg3_payload_size = gdb.parse_and_eval("arg3_payload_size") # Not needed here
184
+
185
+ # Convert the payload type to a structure using some custom mapping function
186
+ type_to_cast = my_custom_payload_type_to_struct(arg2_payload_type)
187
+
188
+ # Cast to a structure pointer
189
+ arg1_payload = arg1_payload.cast(type_to_cast)
190
+
191
+ OUTPUT_LIST.append(gdb2dict.gdb_value_to_dict(arg1_payload))
192
+
193
+ # Return False to not halt (Automatically continue)
194
+ return False
195
+
196
+ with open("output.json", "w") as f:
197
+ f.write("{\"output\": [\n")
198
+ f.write(",\n".join(OUTPUT_LIST))
199
+ f.write("]}")
200
+
201
+ ```
202
+
203
+ Source this script in gdb will set the breakpoint and fill the output file with the parsed values.
204
+
205
+ ```bash
206
+ $ gdb -x my_script.py --batch --nw --nx --return-child-result
207
+ ```
208
+
209
+ `--batch --nw --nx --return-child-result` are recommended for automated gdb scripting,
210
+ see `gdb --help` for more information.
211
+
212
+ Then you can post process the output file with your favorite language.
@@ -0,0 +1,10 @@
1
+ LICENSE.txt
2
+ README.md
3
+ pyproject.toml
4
+ src/gdb2dict/__init__.py
5
+ src/gdb2dict/gdb_value_to_dict.py
6
+ src/gdb2dict.egg-info/PKG-INFO
7
+ src/gdb2dict.egg-info/SOURCES.txt
8
+ src/gdb2dict.egg-info/dependency_links.txt
9
+ src/gdb2dict.egg-info/requires.txt
10
+ src/gdb2dict.egg-info/top_level.txt
@@ -0,0 +1,6 @@
1
+
2
+ [dev]
3
+ check-manifest
4
+
5
+ [test]
6
+ coverage
@@ -0,0 +1 @@
1
+ gdb2dict