|
| 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 ¬eEvt = 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 | +} |
0 commit comments