232 lines
7.0 KiB
C++
232 lines
7.0 KiB
C++
/**
|
|
* Copyright (C) ARM Limited 2013-2015. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
#include "EventsXML.h"
|
|
|
|
#include "CapturedXML.h"
|
|
#include "Logging.h"
|
|
#include "OlyUtility.h"
|
|
#include "SessionData.h"
|
|
|
|
class XMLList {
|
|
public:
|
|
XMLList(XMLList *const prev, mxml_node_t *const node) : mPrev(prev), mNode(node) {}
|
|
|
|
XMLList *getPrev() { return mPrev; }
|
|
mxml_node_t *getNode() const { return mNode; }
|
|
void setNode(mxml_node_t *const node) { mNode = node; }
|
|
|
|
static void free(XMLList *list) {
|
|
while (list != NULL) {
|
|
XMLList *prev = list->getPrev();
|
|
delete list;
|
|
list = prev;
|
|
}
|
|
}
|
|
|
|
private:
|
|
XMLList *const mPrev;
|
|
mxml_node_t *mNode;
|
|
|
|
// Intentionally unimplemented
|
|
XMLList(const XMLList &);
|
|
XMLList &operator=(const XMLList &);
|
|
};
|
|
|
|
mxml_node_t *EventsXML::getTree() {
|
|
#include "events_xml.h" // defines and initializes char events_xml[] and int events_xml_len
|
|
char path[PATH_MAX];
|
|
mxml_node_t *xml = NULL;
|
|
FILE *fl;
|
|
|
|
// Avoid unused variable warning
|
|
(void)events_xml_len;
|
|
|
|
// Load the provided or default events xml
|
|
if (gSessionData->mEventsXMLPath) {
|
|
strncpy(path, gSessionData->mEventsXMLPath, PATH_MAX);
|
|
fl = fopen(path, "r");
|
|
if (fl) {
|
|
xml = mxmlLoadFile(NULL, fl, MXML_NO_CALLBACK);
|
|
fclose(fl);
|
|
}
|
|
}
|
|
if (xml == NULL) {
|
|
logg->logMessage("Unable to locate events.xml, using default");
|
|
xml = mxmlLoadString(NULL, (const char *)events_xml, MXML_NO_CALLBACK);
|
|
}
|
|
|
|
// Append additional events XML
|
|
if (gSessionData->mEventsXMLAppend) {
|
|
fl = fopen(gSessionData->mEventsXMLAppend, "r");
|
|
if (fl == NULL) {
|
|
logg->logError("Unable to open additional events XML %s", gSessionData->mEventsXMLAppend);
|
|
handleException();
|
|
}
|
|
mxml_node_t *append = mxmlLoadFile(NULL, fl, MXML_NO_CALLBACK);
|
|
fclose(fl);
|
|
|
|
mxml_node_t *events = mxmlFindElement(xml, xml, "events", NULL, NULL, MXML_DESCEND);
|
|
if (!events) {
|
|
logg->logError("Unable to find <events> node in the events.xml, please ensure the first two lines of events XML starts with:\n"
|
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
|
"<events>");
|
|
handleException();
|
|
}
|
|
|
|
XMLList *categoryList = NULL;
|
|
XMLList *eventList = NULL;
|
|
{
|
|
// Make list of all categories in xml
|
|
mxml_node_t *node = xml;
|
|
while (true) {
|
|
node = mxmlFindElement(node, xml, "category", NULL, NULL, MXML_DESCEND);
|
|
if (node == NULL) {
|
|
break;
|
|
}
|
|
categoryList = new XMLList(categoryList, node);
|
|
}
|
|
|
|
// Make list of all events in xml
|
|
node = xml;
|
|
while (true) {
|
|
node = mxmlFindElement(node, xml, "event", NULL, NULL, MXML_DESCEND);
|
|
if (node == NULL) {
|
|
break;
|
|
}
|
|
eventList = new XMLList(eventList, node);
|
|
}
|
|
}
|
|
|
|
// Handle events
|
|
for (mxml_node_t *node = mxmlFindElement(append, append, "event", NULL, NULL, MXML_DESCEND),
|
|
*next = mxmlFindElement(node, append, "event", NULL, NULL, MXML_DESCEND);
|
|
node != NULL;
|
|
node = next, next = mxmlFindElement(node, append, "event", NULL, NULL, MXML_DESCEND)) {
|
|
const char *const category = mxmlElementGetAttr(mxmlGetParent(node), "name");
|
|
const char *const title = mxmlElementGetAttr(node, "title");
|
|
const char *const name = mxmlElementGetAttr(node, "name");
|
|
if (category == NULL || title == NULL || name == NULL) {
|
|
logg->logError("Not all event XML nodes have the required title and name and parent name attributes");
|
|
handleException();
|
|
}
|
|
|
|
// Replace any duplicate events
|
|
for (XMLList *event = eventList; event != NULL; event = event->getPrev()) {
|
|
const char *const category2 = mxmlElementGetAttr(mxmlGetParent(event->getNode()), "name");
|
|
const char *const title2 = mxmlElementGetAttr(event->getNode(), "title");
|
|
const char *const name2 = mxmlElementGetAttr(event->getNode(), "name");
|
|
if (category2 == NULL || title2 == NULL || name2 == NULL) {
|
|
logg->logError("Not all event XML nodes have the required title and name and parent name attributes");
|
|
handleException();
|
|
}
|
|
|
|
if (strcmp(category, category2) == 0 && strcmp(title, title2) == 0 && strcmp(name, name2) == 0) {
|
|
logg->logMessage("Replacing counter %s %s: %s", category, title, name);
|
|
mxml_node_t *parent = mxmlGetParent(event->getNode());
|
|
mxmlDelete(event->getNode());
|
|
mxmlAdd(parent, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, node);
|
|
event->setNode(node);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle categories
|
|
for (mxml_node_t *node = strcmp(mxmlGetElement(append), "category") == 0 ? append : mxmlFindElement(append, append, "category", NULL, NULL, MXML_DESCEND),
|
|
*next = mxmlFindElement(node, append, "category", NULL, NULL, MXML_DESCEND);
|
|
node != NULL;
|
|
node = next, next = mxmlFindElement(node, append, "category", NULL, NULL, MXML_DESCEND)) {
|
|
// After replacing duplicate events, a category may be empty
|
|
if (mxmlGetFirstChild(node) == NULL) {
|
|
continue;
|
|
}
|
|
|
|
const char *const name = mxmlElementGetAttr(node, "name");
|
|
if (name == NULL) {
|
|
logg->logError("Not all event XML categories have the required name attribute");
|
|
handleException();
|
|
}
|
|
|
|
// Merge identically named categories
|
|
bool merged = false;
|
|
for (XMLList *category = categoryList; category != NULL; category = category->getPrev()) {
|
|
const char *const name2 = mxmlElementGetAttr(category->getNode(), "name");
|
|
if (name2 == NULL) {
|
|
logg->logError("Not all event XML categories have the required name attribute");
|
|
handleException();
|
|
}
|
|
|
|
if (strcmp(name, name2) == 0) {
|
|
logg->logMessage("Merging category %s", name);
|
|
while (true) {
|
|
mxml_node_t *child = mxmlGetFirstChild(node);
|
|
if (child == NULL) {
|
|
break;
|
|
}
|
|
mxmlAdd(category->getNode(), MXML_ADD_AFTER, mxmlGetLastChild(category->getNode()), child);
|
|
}
|
|
merged = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (merged) {
|
|
continue;
|
|
}
|
|
|
|
// Add new categories
|
|
logg->logMessage("Appending category %s", name);
|
|
mxmlAdd(events, MXML_ADD_AFTER, mxmlGetLastChild(events), node);
|
|
}
|
|
|
|
XMLList::free(eventList);
|
|
XMLList::free(categoryList);
|
|
|
|
mxmlDelete(append);
|
|
}
|
|
|
|
return xml;
|
|
}
|
|
|
|
char *EventsXML::getXML() {
|
|
mxml_node_t *xml = getTree();
|
|
|
|
// Add dynamic events from the drivers
|
|
mxml_node_t *events = mxmlFindElement(xml, xml, "events", NULL, NULL, MXML_DESCEND);
|
|
if (!events) {
|
|
logg->logError("Unable to find <events> node in the events.xml, please ensure the first two lines of events XML are:\n"
|
|
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
|
"<events>");
|
|
handleException();
|
|
}
|
|
for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) {
|
|
driver->writeEvents(events);
|
|
}
|
|
|
|
char *string = mxmlSaveAllocString(xml, mxmlWhitespaceCB);
|
|
mxmlDelete(xml);
|
|
|
|
return string;
|
|
}
|
|
|
|
void EventsXML::write(const char *path) {
|
|
char file[PATH_MAX];
|
|
|
|
// Set full path
|
|
snprintf(file, PATH_MAX, "%s/events.xml", path);
|
|
|
|
char *buf = getXML();
|
|
if (util->writeToDisk(file, buf) < 0) {
|
|
logg->logError("Error writing %s\nPlease verify the path.", file);
|
|
handleException();
|
|
}
|
|
|
|
free(buf);
|
|
}
|