Skip to content

Commit 7e50c47

Browse files
committed
parser-xml-valgrind: separate module for ValgrindTreeDecoder
1 parent 3b2f4d5 commit 7e50c47

File tree

4 files changed

+326
-265
lines changed

4 files changed

+326
-265
lines changed

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ add_library(cs STATIC
8282
parser-json-shchk.cc
8383
parser-json-simple.cc
8484
parser-xml.cc
85+
parser-xml-valgrind.cc
8586
shared-string.cc
8687
version.cc
8788
writer.cc

src/parser-xml-valgrind.cc

Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
/*
2+
* Copyright (C) 2011-2021 Red Hat, Inc.
3+
*
4+
* This file is part of csdiff.
5+
*
6+
* csdiff is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* any later version.
10+
*
11+
* csdiff is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with csdiff. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
#include "parser-xml-valgrind.hh"
21+
22+
// executable of the dynamic linker (used as ELF interpreter)
23+
#ifndef LD_LINUX_SO
24+
# define LD_LINUX_SO "/lib64/ld-linux-x86-64.so.2"
25+
#endif
26+
27+
struct ValgrindTreeDecoder::Private {
28+
Defect defPrototype = Defect("VALGRIND_WARNING");
29+
};
30+
31+
ValgrindTreeDecoder::ValgrindTreeDecoder():
32+
d(new Private)
33+
{
34+
}
35+
36+
ValgrindTreeDecoder::~ValgrindTreeDecoder() = default;
37+
38+
/// hide explicit invocation of dynamic linker from command-line args
39+
bool /* continue */ skipLdArgs(
40+
std::string *pExe,
41+
pt::ptree::const_iterator *pIt,
42+
const pt::ptree::const_iterator itEnd)
43+
{
44+
if (*pExe != LD_LINUX_SO)
45+
return /* continue */ true;
46+
47+
for (bool skipArg = false; *pIt != itEnd; ++(*pIt)) {
48+
if (skipArg) {
49+
skipArg = false;
50+
continue;
51+
}
52+
53+
const std::string argVal = getStringValue(*pIt);
54+
if (argVal == "--preload")
55+
goto skip_arg;
56+
57+
if (argVal == "--argv0")
58+
goto skip_arg;
59+
60+
// record path of the real binary being executed
61+
*pExe = argVal;
62+
++(*pIt);
63+
return /* continue */ (itEnd != *pIt);
64+
65+
skip_arg:
66+
skipArg = true;
67+
}
68+
69+
return /* break */ false;
70+
}
71+
72+
/// read command-line of the executed program
73+
void readExeArgs(
74+
std::string *pExe,
75+
std::string *pArgs,
76+
const pt::ptree *root)
77+
{
78+
const pt::ptree *argsNode;
79+
if (!findChildOf(&argsNode, *root, "args"))
80+
return;
81+
82+
const pt::ptree *argvNode;
83+
if (!findChildOf(&argvNode, *argsNode, "argv"))
84+
return;
85+
86+
// read name of executable
87+
*pExe = valueOf<std::string>(*argvNode, "exe", *pExe);
88+
89+
// read command-line args
90+
pt::ptree::const_iterator it;
91+
for (it = argvNode->begin(); argvNode->end() != it; ++it) {
92+
if (it->first != "arg")
93+
// skip this node
94+
continue;
95+
96+
if (!skipLdArgs(pExe, &it, argvNode->end()))
97+
break;
98+
99+
*pArgs += " ";
100+
*pArgs += getStringValue(it);
101+
}
102+
}
103+
104+
void ValgrindTreeDecoder::readRoot(const pt::ptree *root)
105+
{
106+
// valgrind reports will be at the same level in the XML tree
107+
defList_ = root;
108+
defIter_ = root->begin();
109+
110+
// only valgrind produces this data format
111+
d->defPrototype.tool = "valgrind";
112+
113+
const int pid = valueOf<int>(*root, "pid", 0);
114+
if (!pid)
115+
// insufficient data
116+
return;
117+
118+
// read command-line
119+
std::string exe = "<unknown>";
120+
std::string args;
121+
readExeArgs(&exe, &args, root);
122+
123+
// create a note event in the defect prototype
124+
d->defPrototype.events.push_back(DefEvent("note"));
125+
DefEvent &noteEvt = d->defPrototype.events.back();
126+
noteEvt.fileName = exe;
127+
128+
// record PID and command-line args
129+
std::ostringstream str;
130+
str << "while executing process " << pid;
131+
if (!args.empty())
132+
str << " with arguments:" << args;
133+
noteEvt.msg = str.str();
134+
noteEvt.verbosityLevel = /* note */ 1;
135+
}
136+
137+
/// read valgrind's message
138+
std::string readMsg(const pt::ptree &defNode)
139+
{
140+
const pt::ptree *whatNode;
141+
if (findChildOf(&whatNode, defNode, "what"))
142+
// message found in <what>...</what>
143+
return getStringValue(*whatNode);
144+
145+
if (findChildOf(&whatNode, defNode, "xwhat")
146+
&& findChildOf(&whatNode, *whatNode, "text"))
147+
// message found in <xwhat><text>...</text></xwhat>
148+
return getStringValue(*whatNode);
149+
150+
// message not found
151+
return "<unknown>";
152+
}
153+
154+
/// return true if the given frame is internal to valgrind itself
155+
bool isInternalFrame(const pt::ptree &frameNode)
156+
{
157+
std::string obj = valueOf<std::string>(frameNode, "obj", "");
158+
if (obj.empty())
159+
return false;
160+
161+
static const std::string valgrindPrefix = "/usr/libexec/valgrind/";
162+
static const size_t valgrindPrefixLen = valgrindPrefix.size();
163+
if (obj.size() <= valgrindPrefixLen)
164+
return false;
165+
166+
obj.resize(valgrindPrefixLen);
167+
return (valgrindPrefix == obj);
168+
}
169+
170+
/// go through stack, append "note" events, and update the key event
171+
void readStack(Defect *pDef, const pt::ptree &stackNode)
172+
{
173+
int keyEventBestScore = -1;
174+
175+
for (const pt::ptree::value_type &frame : stackNode) {
176+
const pt::ptree &frameNode = frame.second;
177+
const bool intFrame = isInternalFrame(frameNode);
178+
int keyEventScore = 0;
179+
180+
// initialize "note" event
181+
DefEvent noteEvt("note");
182+
noteEvt.msg = "called from ";
183+
noteEvt.verbosityLevel = /* note */ 1 + static_cast<int>(intFrame);
184+
185+
// read function name if available
186+
const std::string fn = valueOf<std::string>(frameNode, "fn", "");
187+
noteEvt.msg += (fn.empty())
188+
? "here"
189+
: fn + "()";
190+
191+
const pt::ptree *fileNode;
192+
if (findChildOf(&fileNode, frameNode, "file")) {
193+
// read absolute path of the source file
194+
noteEvt.fileName = getStringValue(*fileNode);
195+
const std::string dir = valueOf<std::string>(frameNode, "dir", "");
196+
if (!dir.empty())
197+
noteEvt.fileName = dir + "/" + noteEvt.fileName;
198+
199+
// read line number
200+
noteEvt.line = valueOf<int>(frameNode, "line", 0);
201+
keyEventScore = 8;
202+
}
203+
else if (findChildOf(&fileNode, frameNode, "obj")) {
204+
// pick path of the object file
205+
noteEvt.fileName = getStringValue(*fileNode);
206+
keyEventScore = 4;
207+
}
208+
else if (findChildOf(&fileNode, frameNode, "ip")) {
209+
// pick address of the code in memory
210+
noteEvt.fileName = getStringValue(*fileNode);
211+
keyEventScore = 2;
212+
}
213+
else {
214+
// no location info found --> skip this frame
215+
continue;
216+
}
217+
218+
if (!intFrame && keyEventBestScore < keyEventScore) {
219+
// update key event
220+
keyEventBestScore = keyEventScore;
221+
DefEvent &keyEvent = pDef->events[pDef->keyEventIdx];
222+
keyEvent.fileName = noteEvt.fileName;
223+
keyEvent.line = noteEvt.line;
224+
}
225+
226+
// finally push the "note" event
227+
pDef->events.push_back(noteEvt);
228+
}
229+
}
230+
231+
bool ValgrindTreeDecoder::readNode(Defect *pDef)
232+
{
233+
if (!defList_)
234+
// initialization failed
235+
return false;
236+
237+
pt::ptree::const_iterator it;
238+
do {
239+
if (defList_->end() == defIter_)
240+
// EOF
241+
return false;
242+
243+
// move the iterator after we get the current position
244+
it = defIter_++;
245+
}
246+
while ("error" != it->first);
247+
248+
// the current "error" node representing a single valgrind's report
249+
const pt::ptree &defNode = it->second;
250+
251+
// initialize the defect structure
252+
Defect &def = *pDef;
253+
def = d->defPrototype;
254+
255+
// initialize the key event
256+
def.keyEventIdx = def.events.size();
257+
def.events.push_back(DefEvent("warning"));
258+
DefEvent &keyEvent = def.events.back();
259+
keyEvent.fileName = "<unknown>";
260+
keyEvent.msg = readMsg(defNode);
261+
262+
// read "kind" of the report
263+
const std::string kind = valueOf<std::string>(defNode, "kind", "");
264+
if (!kind.empty())
265+
keyEvent.event += "[" + kind + "]";
266+
267+
// go through stack trace
268+
const pt::ptree *stackNode;
269+
if (findChildOf(&stackNode, defNode, "stack"))
270+
// this invalidates &keyEvent !!!
271+
readStack(pDef, *stackNode);
272+
273+
// read aux valgrind's message if available and insert _after_ the key event
274+
const pt::ptree *auxwhat;
275+
if (findChildOf(&auxwhat, defNode, "auxwhat")) {
276+
DefEvent auxEvent = def.events[def.keyEventIdx];
277+
auxEvent.event = "note";
278+
auxEvent.verbosityLevel = /* note */ 1;
279+
auxEvent.msg = getStringValue(*auxwhat);
280+
def.events.insert(def.events.begin() + def.keyEventIdx + 1, auxEvent);
281+
}
282+
283+
return true;
284+
}

src/parser-xml-valgrind.hh

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright (C) 2021 Red Hat, Inc.
3+
*
4+
* This file is part of csdiff.
5+
*
6+
* csdiff is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* any later version.
10+
*
11+
* csdiff is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with csdiff. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
#ifndef H_GUARD_PARSER_XML_VALGRIND_H
21+
#define H_GUARD_PARSER_XML_VALGRIND_H
22+
23+
#include "abstract-tree.hh"
24+
25+
/// tree decoder for valgrind XML output
26+
class ValgrindTreeDecoder: public AbstractTreeDecoder {
27+
public:
28+
ValgrindTreeDecoder();
29+
~ValgrindTreeDecoder() override;
30+
31+
bool readNode(Defect *def) override;
32+
33+
void readRoot(const pt::ptree *root) override;
34+
35+
private:
36+
struct Private;
37+
std::unique_ptr<Private> d;
38+
};
39+
40+
#endif /* H_GUARD_PARSER_XML_VALGRIND_H */

0 commit comments

Comments
 (0)