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

查看所有标签

猜你喜欢:

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

一个人的电商

一个人的电商

许晓辉 / 电子工业出版社 / 2015-5-1 / CNY 59.00

首次披露电商的运营与操盘内幕,徐小平、梁宁作序,雷军、陈彤、张向东、刘韧、王峰力荐! 这个时代在经历前所未有的转型甚至颠覆,任何行业都将与互联网无缝融合,成为“互联网+”。有很多写电商的书,大多都用浓墨重彩阐释互联网转型的必要性,而讲到如何落地实操则浅尝即止,令人心潮澎拜之后不知如何下手。于是有了这本既有方法论,更重视实操细节的书。 许晓辉,在知名电商公司凡客诚品做过高管,有海......一起来看看 《一个人的电商》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

SHA 加密
SHA 加密

SHA 加密工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具