GDB Custom Commands: Dynamic Arrays

栏目: IT技术 · 发布时间: 4年前

内容简介:As a C shop, we don't have easy access to fancy data structures. Most of the time this isn't a problem - when you work with small data sets, most tasks don't require anything crazy. As we say in the business: arrays are love, arrays are life.So, we have an

Introduction

As a C shop, we don't have easy access to fancy data structures. Most of the time this isn't a problem - when you work with small data sets, most tasks don't require anything crazy. As we say in the business: arrays are love, arrays are life.

So, we have an array module, which is mostly a set of macros using some extra data hidden behind a typed array pointer (more on that approach here ). For the purposes of this post, all you need to know is that we have a function array_size() to, uh, get the size of the array, and we can access array elements using the typical array[index] approach. Anyways, since we use arrays so much, it would be pretty fetch if our favorite debugger GDB could understand our arrays. Let's try the 'print' command, which can be abbreviated as 'p', on an array of integers.

(gdb) print values
$1 = (int *) 0x607020

Ahh, nothing soothes the soul like a hex value. While the memory address of the array is occasionally useful, what I usually want here is the actual values stored in the array. GDB wouldn't be a very useful debugger if it didn't support that.

(gdb) print *values
$1 = 4

As would be expected for those familiar with C, this will only print the first value in the array. We could also print each value individually using values[i] , but that's tedious if we want to see all the values.

(gdb) p array_size(values)
$1 = 6
(gdb) p *values@6
$2 = {4, 8, 15, 16, 23, 42}

Much better. This is the typical way to print multiple sequential values in GDB. Given how often I need to examine data like this, it would be nice to have a simple command - something like 'pa' for 'printarray'. Forcing me to type those 20 extra characters each time should be an OSHA violation.

GDB Custom Commands: Dynamic Arrays

Boilerplate

Luckily, GDB supports extensions written in Python. The 'Hello world' version of a custom command looks like this:

class PrintArray(gdb.Command):
    def __init__(self):
        gdb.Command.__init__(self, "pa", gdb.COMMAND_DATA, gdb.COMPLETE_SYMBOL, True)
    def invoke(self, arg, from_tty):
        print("Hello, world")

PrintArray()

The only noteworthy items so far are the 'pa' name we gave our command and the print() call. We'll save this file as print_array.py.

Now we have to tell GDB to run our script using the source command. We could run this every time we launch GDB, but there is a .gdbinit file that is run automatically on launch. GDB looks for this file in 2 places: the user's home directory and the current working directory. Whichever location you choose, add the following line:

source print_array.py

You can also execute this command while GDB is running, which comes in handy when testing changes to our script. Anyways, we should be up and running.

(gdb) pa
Hello, world

Pack it in, job's finished.

The Meaty Bits

Printing Hello world is so last paragraph. I'd prefer some real logic.

def invoke(self, arg, from_tty):
        args = gdb.string_to_argv(arg)
        if len(args) < 1:
            print("Need array to print")
            return

I said, some real logic.

pointer = args[0]
        size = int(gdb.parse_and_eval("array_size(%s)" % pointer))
        gdb.execute("p %d" % size)
        if size == 0:
            print("Empty")
        else:
            gdb.execute("p *%s@%d" % (pointer, size))

Perfection. parse_and_eval() will let us send an expression back to gdb for evaluation, but, unlike execute() , it returns the result instead of executing like a normal GDB command. Since it returns a gdb.Value type, which for our purposes is less useful than a plain old integer, we cast the result.

(gdb) pa values
$1 = 6
$2 = {4, 8, 15, 16, 23, 42}

Now that's more like it.

The Super Meaty Bits

So, this is nice and all, but most of the time we're not just printing arrays of integers.

struct value {
	int so_much_data[4096];
	int interesting_value;
	int seriously_stop_this_is_way_too_much_data[4095];
};

Printing arrays of structs will work, but the output can be pretty verbose. For large structs or long arrays, we may not even get anything.

(gdb) pa values
$1 = 6
Error occurred in Python command: value requires 196608 bytes, which is more than max-value-size

We can tell gdb to raise the limit, but that won't solve the verbosity problem. Most of the time when this happens, I only want to know the value of 1 or 2 struct members for every item in the array. We should be able to focus on a particular field and exclude all others.

if size == 0:
            print("Empty")
        elif len(args) == 1:
                gdb.execute("p *%s@%d" % (pointer, size))
        else:
            member = args[1]
            statement = "p { "
            for i in range(0, size):
                if i > 0:
                    statement += ", "
                statement += ("(%s)[%d].%s" % (pointer, i, member))
            statement += " }"
            gdb.execute(statement)

Now, if we optionally provide a specific struct member to our custom command, GDB will only print that value for each element. Combining it into one statement instead of calling execute() on each element tells GDB to still print it as an array.

(gdb) pa values interesting_value
$2 = 6
$3 = {4, 8, 15, 16, 23, 42}

To examine multiple members, I just call the command multiple times. Personally, I like the ergonomics of that better than passing multiple struct members to a single command.

Result

The final script, suitably tailored for your copy-and-pasting pleasure:

class PrintArray(gdb.Command):
    def __init__(self):
        gdb.Command.__init__(self, "pa", gdb.COMMAND_DATA, gdb.COMPLETE_SYMBOL, True)
    def invoke(self, arg, from_tty):
        args = gdb.string_to_argv(arg)
        if len(args) < 1:
            print("Need array to print")
            return
        pointer = args[0]
        size = int(gdb.parse_and_eval("array_size(%s)" % pointer))
        gdb.execute("p %d" % size)
        if size == 0:
            print("Empty")
        elif len(args) == 1:
            gdb.execute("p *%s@%d" % (pointer, size))
        else:
            member = args[1]
            statement = "p { "
            for i in range(0, size):
                if i > 0:
                    statement += ", "
                statement += ("(%s)[%d].%s" % (pointer, i, member))
            statement += " }"
            gdb.execute(statement)

PrintArray()

Printing arrays, while useful, isn't incredibly exciting. However, it's a good introduction into a whole new world of extending GDB. So that's it for now. If we spend much more time in python today, we risk catching a serious case of memory bloat.

For further information, GDB's online Python extension documentation is well-written.

No semi-colons were harmed during the production of this post.

<< Back


以上所述就是小编给大家介绍的《GDB Custom Commands: Dynamic Arrays》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

精益数据分析

精益数据分析

[加] 阿利斯泰尔·克罗尔、[加] 本杰明·尤科维奇 / 韩知白、王鹤达 / 人民邮电出版社 / 2014-12 / 79.00元

本书展示了如何验证自己的设想、找到真正的客户、打造能赚钱的产品,以及提升企业知名度。30多个案例分析,全球100多位知名企业家的真知灼见,为你呈现来之不易、经过实践检验的创业心得和宝贵经验,值得每位创业家和企业家一读。 深入理解精益创业、数据分析基础,和数据驱动的思维模式 如何将六个典型的商业模式应用到各种规模的新企业 找到你的第一关键指标 确定底线,找到出发点 在大......一起来看看 《精益数据分析》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具