Jon Medhurst | aaf37a3 | 2013-06-11 12:10:56 +0100 | [diff] [blame] | 1 | /** |
Jon Medhurst | b1d0744 | 2015-05-08 12:04:18 +0100 | [diff] [blame] | 2 | * Copyright (C) ARM Limited 2013-2015. All rights reserved. |
Jon Medhurst | aaf37a3 | 2013-06-11 12:10:56 +0100 | [diff] [blame] | 3 | * |
| 4 | * This program is free software; you can redistribute it and/or modify |
| 5 | * it under the terms of the GNU General Public License version 2 as |
| 6 | * published by the Free Software Foundation. |
| 7 | */ |
| 8 | |
| 9 | #include "EventsXML.h" |
| 10 | |
| 11 | #include "CapturedXML.h" |
| 12 | #include "Logging.h" |
| 13 | #include "OlyUtility.h" |
| 14 | #include "SessionData.h" |
| 15 | |
Jon Medhurst | b1d0744 | 2015-05-08 12:04:18 +0100 | [diff] [blame] | 16 | class XMLList { |
| 17 | public: |
| 18 | XMLList(XMLList *const prev, mxml_node_t *const node) : mPrev(prev), mNode(node) {} |
| 19 | |
| 20 | XMLList *getPrev() { return mPrev; } |
| 21 | mxml_node_t *getNode() const { return mNode; } |
| 22 | void setNode(mxml_node_t *const node) { mNode = node; } |
| 23 | |
| 24 | static void free(XMLList *list) { |
| 25 | while (list != NULL) { |
| 26 | XMLList *prev = list->getPrev(); |
| 27 | delete list; |
| 28 | list = prev; |
| 29 | } |
| 30 | } |
| 31 | |
| 32 | private: |
| 33 | XMLList *const mPrev; |
| 34 | mxml_node_t *mNode; |
| 35 | |
| 36 | // Intentionally unimplemented |
| 37 | XMLList(const XMLList &); |
| 38 | XMLList &operator=(const XMLList &); |
| 39 | }; |
| 40 | |
Jon Medhurst | e31266f | 2014-08-04 15:47:44 +0100 | [diff] [blame] | 41 | mxml_node_t *EventsXML::getTree() { |
Jon Medhurst | aaf37a3 | 2013-06-11 12:10:56 +0100 | [diff] [blame] | 42 | #include "events_xml.h" // defines and initializes char events_xml[] and int events_xml_len |
| 43 | char path[PATH_MAX]; |
Jon Medhurst | b1d0744 | 2015-05-08 12:04:18 +0100 | [diff] [blame] | 44 | mxml_node_t *xml = NULL; |
Jon Medhurst | aaf37a3 | 2013-06-11 12:10:56 +0100 | [diff] [blame] | 45 | FILE *fl; |
| 46 | |
| 47 | // Avoid unused variable warning |
| 48 | (void)events_xml_len; |
| 49 | |
| 50 | // Load the provided or default events xml |
| 51 | if (gSessionData->mEventsXMLPath) { |
| 52 | strncpy(path, gSessionData->mEventsXMLPath, PATH_MAX); |
Jon Medhurst | b1d0744 | 2015-05-08 12:04:18 +0100 | [diff] [blame] | 53 | fl = fopen(path, "r"); |
| 54 | if (fl) { |
| 55 | xml = mxmlLoadFile(NULL, fl, MXML_NO_CALLBACK); |
| 56 | fclose(fl); |
| 57 | } |
Jon Medhurst | aaf37a3 | 2013-06-11 12:10:56 +0100 | [diff] [blame] | 58 | } |
Jon Medhurst | b1d0744 | 2015-05-08 12:04:18 +0100 | [diff] [blame] | 59 | if (xml == NULL) { |
Jon Medhurst | aaf37a3 | 2013-06-11 12:10:56 +0100 | [diff] [blame] | 60 | logg->logMessage("Unable to locate events.xml, using default"); |
Jon Medhurst | 15ce78d | 2014-04-10 09:02:02 +0100 | [diff] [blame] | 61 | xml = mxmlLoadString(NULL, (const char *)events_xml, MXML_NO_CALLBACK); |
Jon Medhurst | aaf37a3 | 2013-06-11 12:10:56 +0100 | [diff] [blame] | 62 | } |
| 63 | |
Jon Medhurst | b1d0744 | 2015-05-08 12:04:18 +0100 | [diff] [blame] | 64 | // Append additional events XML |
| 65 | if (gSessionData->mEventsXMLAppend) { |
| 66 | fl = fopen(gSessionData->mEventsXMLAppend, "r"); |
| 67 | if (fl == NULL) { |
| 68 | logg->logError("Unable to open additional events XML %s", gSessionData->mEventsXMLAppend); |
| 69 | handleException(); |
| 70 | } |
| 71 | mxml_node_t *append = mxmlLoadFile(NULL, fl, MXML_NO_CALLBACK); |
| 72 | fclose(fl); |
| 73 | |
| 74 | mxml_node_t *events = mxmlFindElement(xml, xml, "events", NULL, NULL, MXML_DESCEND); |
| 75 | if (!events) { |
| 76 | logg->logError("Unable to find <events> node in the events.xml, please ensure the first two lines of events XML starts with:\n" |
| 77 | "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" |
| 78 | "<events>"); |
| 79 | handleException(); |
| 80 | } |
| 81 | |
| 82 | XMLList *categoryList = NULL; |
| 83 | XMLList *eventList = NULL; |
| 84 | { |
| 85 | // Make list of all categories in xml |
| 86 | mxml_node_t *node = xml; |
| 87 | while (true) { |
| 88 | node = mxmlFindElement(node, xml, "category", NULL, NULL, MXML_DESCEND); |
| 89 | if (node == NULL) { |
| 90 | break; |
| 91 | } |
| 92 | categoryList = new XMLList(categoryList, node); |
| 93 | } |
| 94 | |
| 95 | // Make list of all events in xml |
| 96 | node = xml; |
| 97 | while (true) { |
| 98 | node = mxmlFindElement(node, xml, "event", NULL, NULL, MXML_DESCEND); |
| 99 | if (node == NULL) { |
| 100 | break; |
| 101 | } |
| 102 | eventList = new XMLList(eventList, node); |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | // Handle events |
| 107 | for (mxml_node_t *node = mxmlFindElement(append, append, "event", NULL, NULL, MXML_DESCEND), |
| 108 | *next = mxmlFindElement(node, append, "event", NULL, NULL, MXML_DESCEND); |
| 109 | node != NULL; |
| 110 | node = next, next = mxmlFindElement(node, append, "event", NULL, NULL, MXML_DESCEND)) { |
| 111 | const char *const category = mxmlElementGetAttr(mxmlGetParent(node), "name"); |
| 112 | const char *const title = mxmlElementGetAttr(node, "title"); |
| 113 | const char *const name = mxmlElementGetAttr(node, "name"); |
| 114 | if (category == NULL || title == NULL || name == NULL) { |
| 115 | logg->logError("Not all event XML nodes have the required title and name and parent name attributes"); |
| 116 | handleException(); |
| 117 | } |
| 118 | |
| 119 | // Replace any duplicate events |
| 120 | for (XMLList *event = eventList; event != NULL; event = event->getPrev()) { |
| 121 | const char *const category2 = mxmlElementGetAttr(mxmlGetParent(event->getNode()), "name"); |
| 122 | const char *const title2 = mxmlElementGetAttr(event->getNode(), "title"); |
| 123 | const char *const name2 = mxmlElementGetAttr(event->getNode(), "name"); |
| 124 | if (category2 == NULL || title2 == NULL || name2 == NULL) { |
| 125 | logg->logError("Not all event XML nodes have the required title and name and parent name attributes"); |
| 126 | handleException(); |
| 127 | } |
| 128 | |
| 129 | if (strcmp(category, category2) == 0 && strcmp(title, title2) == 0 && strcmp(name, name2) == 0) { |
| 130 | logg->logMessage("Replacing counter %s %s: %s", category, title, name); |
| 131 | mxml_node_t *parent = mxmlGetParent(event->getNode()); |
| 132 | mxmlDelete(event->getNode()); |
| 133 | mxmlAdd(parent, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, node); |
| 134 | event->setNode(node); |
| 135 | break; |
| 136 | } |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | // Handle categories |
| 141 | for (mxml_node_t *node = strcmp(mxmlGetElement(append), "category") == 0 ? append : mxmlFindElement(append, append, "category", NULL, NULL, MXML_DESCEND), |
| 142 | *next = mxmlFindElement(node, append, "category", NULL, NULL, MXML_DESCEND); |
| 143 | node != NULL; |
| 144 | node = next, next = mxmlFindElement(node, append, "category", NULL, NULL, MXML_DESCEND)) { |
| 145 | // After replacing duplicate events, a category may be empty |
| 146 | if (mxmlGetFirstChild(node) == NULL) { |
| 147 | continue; |
| 148 | } |
| 149 | |
| 150 | const char *const name = mxmlElementGetAttr(node, "name"); |
| 151 | if (name == NULL) { |
| 152 | logg->logError("Not all event XML categories have the required name attribute"); |
| 153 | handleException(); |
| 154 | } |
| 155 | |
| 156 | // Merge identically named categories |
| 157 | bool merged = false; |
| 158 | for (XMLList *category = categoryList; category != NULL; category = category->getPrev()) { |
| 159 | const char *const name2 = mxmlElementGetAttr(category->getNode(), "name"); |
| 160 | if (name2 == NULL) { |
| 161 | logg->logError("Not all event XML categories have the required name attribute"); |
| 162 | handleException(); |
| 163 | } |
| 164 | |
| 165 | if (strcmp(name, name2) == 0) { |
| 166 | logg->logMessage("Merging category %s", name); |
| 167 | while (true) { |
| 168 | mxml_node_t *child = mxmlGetFirstChild(node); |
| 169 | if (child == NULL) { |
| 170 | break; |
| 171 | } |
| 172 | mxmlAdd(category->getNode(), MXML_ADD_AFTER, mxmlGetLastChild(category->getNode()), child); |
| 173 | } |
| 174 | merged = true; |
| 175 | break; |
| 176 | } |
| 177 | } |
| 178 | |
| 179 | if (merged) { |
| 180 | continue; |
| 181 | } |
| 182 | |
| 183 | // Add new categories |
| 184 | logg->logMessage("Appending category %s", name); |
| 185 | mxmlAdd(events, MXML_ADD_AFTER, mxmlGetLastChild(events), node); |
| 186 | } |
| 187 | |
| 188 | XMLList::free(eventList); |
| 189 | XMLList::free(categoryList); |
| 190 | |
| 191 | mxmlDelete(append); |
| 192 | } |
| 193 | |
Jon Medhurst | e31266f | 2014-08-04 15:47:44 +0100 | [diff] [blame] | 194 | return xml; |
| 195 | } |
| 196 | |
| 197 | char *EventsXML::getXML() { |
| 198 | mxml_node_t *xml = getTree(); |
| 199 | |
Jon Medhurst | aaf37a3 | 2013-06-11 12:10:56 +0100 | [diff] [blame] | 200 | // Add dynamic events from the drivers |
| 201 | mxml_node_t *events = mxmlFindElement(xml, xml, "events", NULL, NULL, MXML_DESCEND); |
| 202 | if (!events) { |
Jon Medhurst | b1d0744 | 2015-05-08 12:04:18 +0100 | [diff] [blame] | 203 | logg->logError("Unable to find <events> node in the events.xml, please ensure the first two lines of events XML are:\n" |
| 204 | "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" |
| 205 | "<events>"); |
Jon Medhurst | aaf37a3 | 2013-06-11 12:10:56 +0100 | [diff] [blame] | 206 | handleException(); |
| 207 | } |
| 208 | for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) { |
| 209 | driver->writeEvents(events); |
| 210 | } |
| 211 | |
Jon Medhurst | e31266f | 2014-08-04 15:47:44 +0100 | [diff] [blame] | 212 | char *string = mxmlSaveAllocString(xml, mxmlWhitespaceCB); |
Jon Medhurst | aaf37a3 | 2013-06-11 12:10:56 +0100 | [diff] [blame] | 213 | mxmlDelete(xml); |
| 214 | |
| 215 | return string; |
| 216 | } |
| 217 | |
Jon Medhurst | e31266f | 2014-08-04 15:47:44 +0100 | [diff] [blame] | 218 | void EventsXML::write(const char *path) { |
Jon Medhurst | aaf37a3 | 2013-06-11 12:10:56 +0100 | [diff] [blame] | 219 | char file[PATH_MAX]; |
| 220 | |
| 221 | // Set full path |
| 222 | snprintf(file, PATH_MAX, "%s/events.xml", path); |
Jon Medhurst | e31266f | 2014-08-04 15:47:44 +0100 | [diff] [blame] | 223 | |
| 224 | char *buf = getXML(); |
Jon Medhurst | aaf37a3 | 2013-06-11 12:10:56 +0100 | [diff] [blame] | 225 | if (util->writeToDisk(file, buf) < 0) { |
Jon Medhurst | b1d0744 | 2015-05-08 12:04:18 +0100 | [diff] [blame] | 226 | logg->logError("Error writing %s\nPlease verify the path.", file); |
Jon Medhurst | aaf37a3 | 2013-06-11 12:10:56 +0100 | [diff] [blame] | 227 | handleException(); |
| 228 | } |
| 229 | |
| 230 | free(buf); |
| 231 | } |