我想调试 C++ 应用程序核心和 gcore 文件。
这包括为二进制数据存储库中的相关数据创建报告。
这些存储库基于 STL 容器(std::map<>、std::set<>、std::vector<>、...)
为此,我使用自己的“通用”Python 帮助程序自动遍历 STL 容器,调用回调函数来处理数据,并创建数据报告。
这是一个例子:
测试c++程序:
#include <string>
#include <map>
#include <iostream>
struct DemoStruct
{
DemoStruct(int arg1, double arg2, std::string arg3, std::string arg4, char arg5, double arg6)
: wantSeeThis_1(arg1),
wantIgnoreThis_1(arg2),
wantSeeThis_2(arg3),
wantIgnoreThis_2(arg4),
wantIgnoreThis_3(arg5),
wantSeeThis_3(arg6)
{ };
~DemoStruct() = default;
int wantSeeThis_1;
double wantIgnoreThis_1;
std::string wantSeeThis_2;
std::string wantIgnoreThis_2;
char wantIgnoreThis_3;
double wantSeeThis_3;
};
std::map<int, DemoStruct> staticData = {
{ 1, {1, 2.0, "hello", "world", 'a', 3.0 } },
{ 2, {10, 3.2, "hi", "world", 'b', 0.3 } },
{ 3, {50, 4.7, "salute", "world", 'c', 11.5 } },
};
int main(int, char**)
{
// set breakpoint here and inspect data
return 0;
}
编译它:
> g++ -std=c++11 -o main -g3 -O0 main.cpp
使用 #2 python 脚本进行调试:
文件#1:
> cat stlhelper.py
# -*- coding: utf-8 -*-
import gdb
import sys
import re
import itertools
import six
#
# base class for PrintGenricXY
#
class PrintGenericCommon (gdb.Function):
"""print generic common"""
def __init__ (self):
super (PrintGenericCommon, self).__init__ ("PrintGenericList")
#
# data is hosted by the subject and we don't know their representation
# subject.ElemItemString() is responsible to format properly (might be complex data)
#
def PrintResult(self, subject):
# calculate the max sizes per element of all lines for alignment
sizes = subject.sizesDef
for x in subject.result:
for key, value in six.iteritems(sizes):
valStr = subject.ElemItemString(x[key])
if len(valStr) > value:
sizes[key] = len(valStr)
totalSize = 0
for key, value in six.iteritems(sizes):
sizes[key] += 2
totalSize += sizes[key]
print("-" * totalSize)
for i in range(len(subject.result)):
myStr = ''
for j in range(len(subject.elements)):
setSize=sizes[subject.elements[j]]
if (j == len(subject.elements) - 1):
setSize=0 # don't auto-align the last element, which often can have some large elements in the list
myStr += "%-*s" % (setSize, subject.ElemItemString(subject.result[i][subject.elements[j]]))
print (myStr)
# print optional __VERBOSE data in a next line
if '__VERBOSE' in subject.result[i]:
str = subject.ElemItemString(subject.result[i]['__VERBOSE'])
lines = str.splitlines()
for line in lines:
print ('\t' + line)
if len(str) > 0 and str[-1] == '\n':
print('')
if i == 0:
print ("-" * totalSize)
#
# generic printer for std::map<> and std::set<>
#
class PrintGenericMapOrSet (PrintGenericCommon):
"""print generic map/set"""
regex = re.compile('\$count')
maxNoResults = -1
def __init__ (self):
super (PrintGenericCommon, self).__init__ ("PrintGenericMapOrSet")
def setMaxNoResults(self, maxNoResults):
self.maxNoResults = maxNoResults
def invokeCore(self, headlines, map, valueType, isConst, isPointer, valueHandler):
collectedLines = []
map = map['_M_t']['_M_impl']
count = map['_M_node_count']
for x in headlines:
collectedLines.append(self.regex.sub(str(count), x))
node = map['_M_header']['_M_left']
valuetype = gdb.lookup_type (valueType)
if isConst:
valuetype = valuetype.const()
if isPointer:
valuetype = valuetype.pointer()
nodetype = gdb.lookup_type('std::_Rb_tree_node < %s >' % valuetype)
nodetype = nodetype.pointer()
i = 0
while i < count:
collectedLines.append(valueHandler(i, node.cast (nodetype).dereference()['_M_value_field'], node))
if node.dereference()['_M_right']:
node = node.dereference()['_M_right']
while node.dereference()['_M_left']:
node = node.dereference()['_M_left']
else:
parent = node.dereference()['_M_parent']
while node == parent.dereference()['_M_right']:
node = parent
parent = parent.dereference()['_M_parent']
if node.dereference()['_M_right'] != parent:
node = parent
i += 1
if self.maxNoResults > -1 and i >= self.maxNoResults:
break;
return "\n".join(collectedLines)
def invoke (self, headlines, mapLoc, valueType, valueHandler, typeWrapper):
map = gdb.parse_and_eval (mapLoc)
return self.invokeCore(headlines, map, valueType, valueHandler, typeWrapper);
#
# generic printer for std::map<>
#
class PrintGenericMap (gdb.Function):
"""print generic map"""
handler = PrintGenericMapOrSet()
def __init__ (self):
super (PrintGenericMap, self).__init__ ("PrintGenericMap")
def setMaxNoResults(self, maxNoResults):
return self.handler.setMaxNoResults(maxNoResults)
def invokeCore(self, headlines, map, valueType, valueHandler):
return self.handler.invokeCore(headlines, map, 'std::pair' + valueType, False, False, valueHandler)
def invoke (self, headlines, mapLoc, valueType, valueHandler, maxNoResults):
return self.handler.invoke(headlines, mapLoc, 'std::pair' + valueType, False, False, valueHandler);
def PrintResult(self, subject):
return self.handler.PrintResult(subject)
#
# generic printer for std::set<>
#
class PrintGenericSet (gdb.Function):
"""print generic set"""
handler = PrintGenericMapOrSet()
def __init__ (self):
super (PrintGenericSet, self).__init__ ("PrintGenericSet")
def setMaxNoResults(self, maxNoResults):
return self.handler.setMaxNoResults(maxNoResults)
def invokeCore(self, headlines, map, valueType, isConst, isPointer, valueHandler):
return self.handler.invokeCore(headlines, map, valueType, isConst, isPointer, valueHandler)
def invoke (self, headlines, mapLoc, valueType, isConst, isPointer, valueHandler, maxNoResults):
return self.handler.invoke(headlines, mapLoc, valueType, isConst, isPointer, valueHandler);
def PrintResult(self, subject):
return self.handler.PrintResult(subject)
文件#2:
> cat main.py
# -*- coding: utf-8 -*-
import gdb, platform, six, copy
from stlhelper import PrintGenericMap, PrintGenericSet
class PrintStaticData (gdb.Command):
"""Print STL data demo.\n"""
printer = None
result = []
headline = {}
sizesDef = {}
elements = [
'key',
'see_1',
'see_2',
'see_3',
]
errorEntry = {
'key' : '?',
'see_1' : '?',
'see_2' : '?',
'see_3' : '?',
}
def __init__ (self):
super (PrintStaticData, self).__init__ ("PrintStaticData", gdb.COMMAND_USER)
self.headline = {
'key' : 'Key',
'see_1' : 'Value #1',
'see_2' : 'Value #2',
'see_3' : 'Value #3',
}
def ReInit(self):
if(platform.python_version().startswith("2")):
for key, value in six.iteritems(self.headline):
self.sizesDef[key] = 0
else:
for key in self.headline.keys():
self.sizesDef[key] = 0
self.result = []
self.result.append(self.headline)
def ElemItemString(self, elem):
return elem
def Traverse(self, index, pair, node = None):
first = pair['first']
second = pair['second']
newEntry = copy.deepcopy(self.errorEntry)
newEntry['key'] = str(first)
newEntry['see_1'] = str(second['wantSeeThis_1'])
newEntry['see_2'] = str(second['wantSeeThis_2'])
newEntry['see_3'] = str(second['wantSeeThis_3'])
self.result.append(newEntry)
return ''
def invoke (self, arg, from_tty):
self.ReInit()
self.printer = PrintGenericMap()
data = gdb.parse_and_eval("staticData")
result = self.printer.invokeCore(
[],
data,
'<int const, DemoStruct>',
self.Traverse)
self.printer.PrintResult(self)
PrintStaticData()
.gdbinit 文件(根据您自己的路径进行调整):
cat $HOME/.gdbinit
set print object
set print pretty
set print static off
set pagination off
set auto-load safe-path /
python
sys.path.insert(0, "/home/me/TEST")
import main
end
set history filename ~/.gdb_history
set history save
在“gdb”中执行演示,例如海湾合作委员会 4.8.5
> gdb main
(gdb) break 35
Breakpoint 1 at 0x400c18: file main.cpp, line 35.
(gdb) r
Starting program: /home/me/TEST/main
Breakpoint 1, main () at main.cpp:35
35 return 0;
(gdb) PrintStaticData
----------------------------------------------
Key Value #1 Value #2 Value #3
----------------------------------------------
1 1 "hello" 3
2 10 "hi" 0.29999999999999999
3 50 "salute" 11.5
到目前为止一切顺利。
现在的问题是,它不再适用于 gcc 9.4.0。 :-(
原因是 STL 类型 std::map<> 的内部数据结构发生了变化。
我的 python 助手并不是真正的“通用”——它们依赖于具体的 STL 实现。
使用 gcc 9.4.0 我打了
(gdb) PrintStaticData
Python Exception <class 'gdb.error'> There is no member or method named _M_value_field.:
Error occurred in Python: There is no member or method named _M_value_field.
不再有 _M_value_field - 我在 stdlhelper.py 中使用它:
collectedLines.append(valueHandler(i, node.cast (nodetype).dereference()['_M_value_field'], node))
所以我可以尝试更新Python代码以处理新的STL实现。
但我认为(希望)gcc python STL 工具箱中已经有一些通用工具可以执行相同的操作:
遍历任何 STL 容器 - 无需了解其实现 - 并调用回调函数来处理存储在容器中的每个元素。
gcc python helpers 可以提供这样的功能吗?
也许有一些文档/指南的链接?
非常感谢用户ssbssa!
此替换 main.py 用于“PrintStaticDataV2”,打印与“PrintStaticData”相同的结果。
但它不再需要在我的 stlhelper.py 中自行编写代码。
相反,它基于 https://sourceware.org/gdb/current/onlinedocs/gdb.html/Pretty-Printing-API.html
# -*- coding: utf-8 -*-
import gdb, platform, six, copy
from stlhelper import PrintGenericMap, PrintGenericSet
class PrintStaticData (gdb.Command):
"""Print STL data demo.\n"""
printer = None
result = []
headline = {}
sizesDef = {}
elements = [
'key',
'see_1',
'see_2',
'see_3',
]
errorEntry = {
'key' : '?',
'see_1' : '?',
'see_2' : '?',
'see_3' : '?',
}
def __init__ (self):
super (PrintStaticData, self).__init__ ("PrintStaticData", gdb.COMMAND_USER)
self.headline = {
'key' : 'Key',
'see_1' : 'Value #1',
'see_2' : 'Value #2',
'see_3' : 'Value #3',
}
def ReInit(self):
if(platform.python_version().startswith("2")):
for key, value in six.iteritems(self.headline):
self.sizesDef[key] = 0
else:
for key in self.headline.keys():
self.sizesDef[key] = 0
self.result = []
self.result.append(self.headline)
def ElemItemString(self, elem):
return elem
def Traverse(self, index, pair, node = None):
first = pair['first']
second = pair['second']
newEntry = copy.deepcopy(self.errorEntry)
newEntry['key'] = str(first)
newEntry['see_1'] = str(second['wantSeeThis_1'])
newEntry['see_2'] = str(second['wantSeeThis_2'])
newEntry['see_3'] = str(second['wantSeeThis_3'])
self.result.append(newEntry)
return ''
def invoke (self, arg, from_tty):
self.ReInit()
self.printer = PrintGenericMap()
data = gdb.parse_and_eval("staticData")
result = self.printer.invokeCore(
[],
data,
'<int const, DemoStruct>',
self.Traverse)
self.printer.PrintResult(self)
PrintStaticData()
class PrintStaticDataV2 (gdb.Command):
"""Print STL data demo version #2.\n"""
printer = None
result = []
headline = {}
sizesDef = {}
elements = [
'key',
'see_1',
'see_2',
'see_3',
]
errorEntry = {
'key' : '?',
'see_1' : '?',
'see_2' : '?',
'see_3' : '?',
}
def __init__ (self):
super (PrintStaticDataV2, self).__init__ ("PrintStaticDataV2", gdb.COMMAND_USER)
self.headline = {
'key' : 'Key',
'see_1' : 'Value #1',
'see_2' : 'Value #2',
'see_3' : 'Value #3',
}
def ReInit(self):
if(platform.python_version().startswith("2")):
for key, value in six.iteritems(self.headline):
self.sizesDef[key] = 0
else:
for key in self.headline.keys():
self.sizesDef[key] = 0
self.result = []
self.result.append(self.headline)
def ElemItemString(self, elem):
return elem
def Traverse(self, pair):
first = pair[0]
second = pair[1]
newEntry = copy.deepcopy(self.errorEntry)
newEntry['key'] = str(first)
newEntry['see_1'] = str(second['wantSeeThis_1'])
newEntry['see_2'] = str(second['wantSeeThis_2'])
newEntry['see_3'] = str(second['wantSeeThis_3'])
self.result.append(newEntry)
return ''
def invoke (self, arg, from_tty):
self.ReInit()
self.printer = PrintGenericMap()
data = gdb.parse_and_eval("staticData")
pp = gdb.default_visualizer(data)
no = 0
key = None
for element in pp.children():
if (no % 2) == 0:
key = element[1]
else:
self.Traverse((key, element[1]))
no += 1
print('container has #' + str(no) + ' children')
self.printer.PrintResult(self)
PrintStaticDataV2()
结果:
(gdb) PrintStaticData
----------------------------------------------
Key Value #1 Value #2 Value #3
----------------------------------------------
1 1 "hello" 3
2 10 "hi" 0.29999999999999999
3 50 "salute" 11.5
(gdb) PrintStaticDataV2
container has #6 children
----------------------------------------------
Key Value #1 Value #2 Value #3
----------------------------------------------
1 1 "hello" 3
2 10 "hi" 0.29999999999999999
3 50 "salute" 11.5