/* This file is part of VoltDB. * Copyright (C) 2008-2017 VoltDB Inc. * * This file contains original code and/or modifications of original code. * Any modifications made by VoltDB Inc. are licensed under the following * terms and conditions: * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ /** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with this * work for additional information regarding copyright ownership. The ASF * licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */package org.voltdb.regressionsuites; import java.io.File; import java.util.ArrayList; import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * This class manages valgrind XML output. The main entry point is the static * function processValgrindOutput. */ public class ValgrindXMLParser { static class StackTrace { public List<String> m_stackFrames = new ArrayList<>(); } // // Valgrind will have written its output to an xml file. We // open that file here and process it. If there are memory // errors we set the member variable m_allHeapBlocksFreed to // false. If there are other errors we put the error messages // in the list m_valgrindErrors. // static class ValgrindError { public String m_kind; public String m_what; public int m_leakedBytes = -1; public int m_leakedBlocks = -1; public List<StackTrace> m_stacks = new ArrayList<>(); @Override public String toString() { StringBuffer sb = new StringBuffer(); sb.append(m_kind).append(" error:\n") .append(" ").append(m_what).append("\n"); if (0 < m_leakedBytes) { sb.append(" leakedBytes: ").append(m_leakedBytes).append("\n"); } if (0 < m_leakedBlocks) { sb.append(" leakedBlocks: ").append(m_leakedBlocks).append("\n"); } sb.append(" Stacks:\n"); if (m_stacks.size() == 0) { sb.append(" None!!\n"); } else { for (int stackno = 0; stackno < m_stacks.size(); stackno += 1) { StackTrace st = m_stacks.get(stackno); int frameNo = 0; for (String frame : st.m_stackFrames) { sb.append(String.format(" %2d.) %s\n", frameNo, frame)); frameNo += 1; } } } return sb.toString(); } } private static StackTrace readStackTrace(Node stackTrace) { StackTrace answer = new StackTrace(); for (Node frame = stackTrace.getFirstChild(); frame != null; frame = frame.getNextSibling()) { String nodeName = frame.getNodeName(); if ( ! "frame".equals(nodeName)) { continue; } String ip = "<undefined ip>"; String fn = "<unknown function>"; String dir = "<unknown directory>"; String file = "<unknown file>"; String lineNo = "<unknown line number>"; for (Node info = frame.getFirstChild(); info != null; info = info.getNextSibling()) { String name = info.getNodeName(); String value = info.getTextContent().trim(); switch (name) { case "ip": ip = value; break; case "fn": fn = value; break; case "dir": dir = value; break; case "file": file = value; break; case "line": lineNo = value; break; default: break; } } answer.m_stackFrames.add(String.format("%s: %s@%s/%s: line %s", ip, fn, dir, file, lineNo)); } return answer; }; private static String readExtendedWhat(Node xwhat) { String text = "<unknown cause>"; String lbytes = null; String lblks = null; for (Node sib = xwhat.getFirstChild(); sib != null; sib = sib.getNextSibling()) { String name = sib.getNodeName(); String value = sib.getTextContent().trim(); switch (name) { case "leakedbytes": lbytes = "Leaked bytes: " + Integer.valueOf(value); break; case "leakedblocks": lblks = "Leaked blocks: " + Integer.valueOf(value); break; case "text": text = value; break; default: ; } } return "Error: " + text + (lbytes != null ? (", " + lbytes) : "") + ((lblks != null) ? (", " + lblks) : ""); } /** * Given an output file from valgrind, and a list to which we want * to add errors, parse the file and add any errors found in the file * to the end of the list. We do this this way, rather than just * returning a list, because this list is shared with another thread. * So, it has to be synchronized. * * Note that this does not delete the valgrindOutputFile. The caller * is responsible for this. * * @param valgrindOutputFile * @param valgrindErrors */ public static void processValgrindOutput(File valgrindOutputFile, List<String> valgrindErrors) { try { DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder(); Document doc = docBuilder.parse (valgrindOutputFile); NodeList errors = doc.getElementsByTagName("error"); for (int idx = 0; idx < errors.getLength(); idx += 1) { Node error = errors.item(idx); ValgrindError vgerr = new ValgrindError(); for (Node sib = error.getFirstChild(); sib != null; sib = sib.getNextSibling()) { String name = sib.getNodeName(); String value = sib.getTextContent().trim(); switch (name) { case "kind": vgerr.m_kind = value; break; case "what": vgerr.m_what = value; break; case "xwhat": vgerr.m_what = readExtendedWhat(sib); break; case "leakedbytes": vgerr.m_leakedBytes = Integer.valueOf(value); break; case "leakedblocks": vgerr.m_leakedBlocks = Integer.valueOf(value); break; case "stack": vgerr.m_stacks.add(readStackTrace(sib)); break; default: break; } } valgrindErrors.add(vgerr.toString()); } } catch (Exception ex) { ex.printStackTrace(); return; } } }