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》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

深入应用C++11

深入应用C++11

祁宇 / 机械工业出版社 / 2015-5 / 79

在StackOverflow的最近一次世界性调查中,C++11在所有的编程语言中排名第二, C++11受到程序员的追捧是毫不意外的,因为它就像C++之父Bjarne Stroustrup说的:它看起来就像一门新的语言。C++11新增加了相当多的现代编程语言的特性,相比C++98/03,它在生产力、安全性、性能和易用性上都有了大幅提高。比如auto和decltype让我们从书写冗长的类型和繁琐的类型......一起来看看 《深入应用C++11》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

SHA 加密
SHA 加密

SHA 加密工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器