From d18974d3f05535eda819f2d0b92a9d49719b0f26 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Thu, 10 May 2012 14:15:56 +0100 Subject: [PATCH] gator: Version 5.10 New gator release (build 1385) for ARM DS-5 v5.10 Signed-off-by: Jon Medhurst --- daemon/Android.mk | 13 +- daemon/CapturedXML.cpp | 92 +- daemon/CapturedXML.h | 8 +- daemon/Child.cpp | 56 +- daemon/Child.h | 8 +- daemon/Collector.cpp | 98 +- daemon/Collector.h | 8 +- daemon/ConfigurationXML.cpp | 136 +- daemon/ConfigurationXML.h | 10 +- daemon/Fifo.cpp | 229 +- daemon/Fifo.h | 74 +- daemon/LocalCapture.cpp | 30 +- daemon/LocalCapture.h | 4 +- daemon/Makefile | 14 +- daemon/OlySocket.cpp | 30 +- daemon/OlySocket.h | 4 +- daemon/OlyUtility.cpp | 111 +- daemon/OlyUtility.h | 5 +- daemon/RequestXML.cpp | 61 - daemon/RequestXML.h | 33 - daemon/Sender.cpp | 56 +- daemon/Sender.h | 8 +- daemon/SessionData.cpp | 23 +- daemon/SessionData.h | 25 +- daemon/SessionXML.cpp | 107 +- daemon/SessionXML.h | 8 +- daemon/StreamlineSetup.cpp | 166 +- daemon/StreamlineSetup.h | 4 +- daemon/XMLOut.cpp | 175 -- daemon/XMLOut.h | 45 - daemon/XMLReader.cpp | 164 -- daemon/XMLReader.h | 27 - daemon/configuration.xml | 86 +- daemon/events-ARM11.xml | 8 +- daemon/events-ARM11MPCore.xml | 10 +- daemon/events-Cortex-A15.xml | 65 +- daemon/events-Cortex-A5.xml | 14 +- daemon/events-Cortex-A7.xml | 31 +- daemon/events-Cortex-A8.xml | 19 +- daemon/events-Cortex-A9.xml | 39 +- daemon/events-Krait-architected.xml | 15 +- daemon/events-L2C-310.xml | 30 +- daemon/events-Linux.xml | 21 +- daemon/events-Mali-400.xml | 4 +- daemon/events-Scorpion.xml | 185 +- daemon/events-ScorpionMP.xml | 151 +- daemon/main.cpp | 95 +- daemon/mxml/COPYING | 507 +++++ daemon/mxml/config.h | 96 + daemon/mxml/mxml-attr.c | 319 +++ daemon/mxml/mxml-entity.c | 460 ++++ daemon/mxml/mxml-file.c | 3080 +++++++++++++++++++++++++++ daemon/mxml/mxml-get.c | 471 ++++ daemon/mxml/mxml-index.c | 662 ++++++ daemon/mxml/mxml-node.c | 807 +++++++ daemon/mxml/mxml-private.c | 331 +++ daemon/mxml/mxml-private.h | 50 + daemon/mxml/mxml-search.c | 287 +++ daemon/mxml/mxml-set.c | 349 +++ daemon/mxml/mxml-string.c | 476 +++++ daemon/mxml/mxml.h | 329 +++ driver/Makefile | 11 - driver/gator.h | 1 + driver/gator_annotate.c | 184 +- driver/gator_backtrace.c | 6 +- driver/gator_cookies.c | 10 +- driver/gator_ebs.c | 64 +- driver/gator_events_armv6.c | 16 +- driver/gator_events_armv7.c | 15 +- driver/gator_events_block.c | 6 +- driver/gator_events_irq.c | 19 +- driver/gator_events_mali.c | 165 +- driver/gator_events_meminfo.c | 9 +- driver/gator_events_mmaped.c | 77 +- driver/gator_events_net.c | 17 +- driver/gator_events_perf_pmu.c | 16 +- driver/gator_events_power.c | 178 -- driver/gator_events_scorpion.c | 18 +- driver/gator_fs.c | 8 + driver/gator_hrtimer_gator.c | 23 +- driver/gator_main.c | 471 ++-- driver/gator_marshaling.c | 239 +++ driver/gator_pack.c | 98 - driver/gator_trace_gpu.c | 164 +- driver/gator_trace_power.c | 160 ++ driver/gator_trace_sched.c | 182 +- 86 files changed, 10505 insertions(+), 2481 deletions(-) mode change 100644 => 100755 daemon/Android.mk mode change 100644 => 100755 daemon/OlySocket.cpp mode change 100644 => 100755 daemon/OlySocket.h mode change 100644 => 100755 daemon/OlyUtility.cpp mode change 100644 => 100755 daemon/OlyUtility.h delete mode 100644 daemon/RequestXML.cpp delete mode 100644 daemon/RequestXML.h delete mode 100644 daemon/XMLOut.cpp delete mode 100644 daemon/XMLOut.h delete mode 100644 daemon/XMLReader.cpp delete mode 100644 daemon/XMLReader.h mode change 100644 => 100755 daemon/events-Cortex-A7.xml mode change 100644 => 100755 daemon/events-Krait-architected.xml create mode 100644 daemon/mxml/COPYING create mode 100644 daemon/mxml/config.h create mode 100644 daemon/mxml/mxml-attr.c create mode 100644 daemon/mxml/mxml-entity.c create mode 100644 daemon/mxml/mxml-file.c create mode 100644 daemon/mxml/mxml-get.c create mode 100644 daemon/mxml/mxml-index.c create mode 100644 daemon/mxml/mxml-node.c create mode 100644 daemon/mxml/mxml-private.c create mode 100644 daemon/mxml/mxml-private.h create mode 100644 daemon/mxml/mxml-search.c create mode 100644 daemon/mxml/mxml-set.c create mode 100644 daemon/mxml/mxml-string.c create mode 100644 daemon/mxml/mxml.h mode change 100644 => 100755 driver/gator_events_mali.c mode change 100644 => 100755 driver/gator_events_perf_pmu.c delete mode 100644 driver/gator_events_power.c mode change 100644 => 100755 driver/gator_hrtimer_gator.c create mode 100755 driver/gator_marshaling.c create mode 100755 driver/gator_trace_power.c diff --git a/daemon/Android.mk b/daemon/Android.mk old mode 100644 new mode 100755 index 14aeda2..3ab55b9 --- a/daemon/Android.mk +++ b/daemon/Android.mk @@ -16,13 +16,20 @@ LOCAL_SRC_FILES:= \ main.cpp \ OlySocket.cpp \ OlyUtility.cpp \ - RequestXML.cpp \ Sender.cpp \ SessionData.cpp \ SessionXML.cpp \ StreamlineSetup.cpp \ - XMLOut.cpp \ - XMLReader.cpp + mxml/mxml-attr.c \ + mxml/mxml-entity.c \ + mxml/mxml-file.c \ + mxml/mxml-get.c \ + mxml/mxml-index.c \ + mxml/mxml-node.c \ + mxml/mxml-private.c \ + mxml/mxml-search.c \ + mxml/mxml-set.c \ + mxml/mxml-string.c LOCAL_C_INCLUDES := $(LOCAL_PATH) diff --git a/daemon/CapturedXML.cpp b/daemon/CapturedXML.cpp index 51076dd..f02ed2a 100644 --- a/daemon/CapturedXML.cpp +++ b/daemon/CapturedXML.cpp @@ -22,13 +22,15 @@ CapturedXML::CapturedXML() { CapturedXML::~CapturedXML() { } -const char* CapturedXML::getXML() { +mxml_node_t* CapturedXML::getTree() { bool perfCounters = false; + mxml_node_t *xml; + mxml_node_t *captured; + mxml_node_t *target; + mxml_node_t *counters; + mxml_node_t *counter; int x; - clearXmlString(); - xmlHeader(); - for (x=0; xmPerfCounterEnabled[x]) { perfCounters = true; @@ -36,62 +38,86 @@ const char* CapturedXML::getXML() { } } - startElement("captured"); - attributeInt("version", 1); - attributeInt("protocol", PROTOCOL_VERSION); + xml = mxmlNewXML("1.0"); + + captured = mxmlNewElement(xml, "captured"); + mxmlElementSetAttr(captured, "version", "1"); + mxmlElementSetAttrf(captured, "protocol", "%d", PROTOCOL_VERSION); if (gSessionData->mBytes > 0) { // Send the following only after the capture is complete if (time(NULL) > 1267000000) { // If the time is reasonable (after Feb 23, 2010) - attributeUInt("created", time(NULL)); // Valid until the year 2038 + mxmlElementSetAttrf(captured, "created", "%lu", time(NULL)); // Valid until the year 2038 } - attributeUInt("bytes", gSessionData->mBytes); + mxmlElementSetAttrf(captured, "bytes", "%d", gSessionData->mBytes); } - startElement("target"); - attributeString("name", gSessionData->mCoreName); - attributeInt("sample_rate", gSessionData->mSampleRate); - attributeInt("cores", gSessionData->mCores); - endElement("target"); + + target = mxmlNewElement(captured, "target"); + mxmlElementSetAttr(target, "name", gSessionData->mCoreName); + mxmlElementSetAttrf(target, "sample_rate", "%d", gSessionData->mSampleRate); + mxmlElementSetAttrf(target, "cores", "%d", gSessionData->mCores); + if (perfCounters) { - startElement("counters"); + counters = mxmlNewElement(captured, "counters"); for (x = 0; x < MAX_PERFORMANCE_COUNTERS; x++) { if (gSessionData->mPerfCounterEnabled[x]) { - startElement("counter"); - attributeString("title", gSessionData->mPerfCounterTitle[x]); - attributeString("name", gSessionData->mPerfCounterName[x]); - attributeHex8("color", gSessionData->mPerfCounterColor[x]); - attributeHex8("key", gSessionData->mPerfCounterKey[x]); - attributeString("type", gSessionData->mPerfCounterType[x]); - attributeHex8("event", gSessionData->mPerfCounterEvent[x]); + counter = mxmlNewElement(counters, "counter"); + mxmlElementSetAttr(counter, "title", gSessionData->mPerfCounterTitle[x]); + mxmlElementSetAttr(counter, "name", gSessionData->mPerfCounterName[x]); + mxmlElementSetAttrf(counter, "color", "0x%08x", gSessionData->mPerfCounterColor[x]); + mxmlElementSetAttrf(counter, "key", "0x%08x", gSessionData->mPerfCounterKey[x]); + mxmlElementSetAttr(counter, "type", gSessionData->mPerfCounterType[x]); + mxmlElementSetAttrf(counter, "event", "0x%08x", gSessionData->mPerfCounterEvent[x]); if (gSessionData->mPerfCounterPerCPU[x]) { - attributeBool("per_cpu", true); + mxmlElementSetAttr(counter, "per_cpu", "yes"); } if (strlen(gSessionData->mPerfCounterOperation[x]) > 0) { - attributeString("operation", gSessionData->mPerfCounterOperation[x]); + mxmlElementSetAttr(counter, "operation", gSessionData->mPerfCounterOperation[x]); } if (gSessionData->mPerfCounterCount[x] > 0) { - attributeInt("count", gSessionData->mPerfCounterCount[x]); + mxmlElementSetAttrf(counter, "count", "%d", gSessionData->mPerfCounterCount[x]); + } + if (gSessionData->mPerfCounterLevel[x]) { + mxmlElementSetAttr(counter, "level", "yes"); + } + if (strlen(gSessionData->mPerfCounterAlias[x]) > 0) { + mxmlElementSetAttr(counter, "alias", gSessionData->mPerfCounterAlias[x]); } - attributeString("description", gSessionData->mPerfCounterDescription[x]); - endElement("counter"); + if (strlen(gSessionData->mPerfCounterDisplay[x]) > 0) { + mxmlElementSetAttr(counter, "display", gSessionData->mPerfCounterDisplay[x]); + } + if (strlen(gSessionData->mPerfCounterUnits[x]) > 0) { + mxmlElementSetAttr(counter, "units", gSessionData->mPerfCounterUnits[x]); + } + if (gSessionData->mPerfCounterAverageSelection[x]) { + mxmlElementSetAttr(counter, "average_selection", "yes"); + } + mxmlElementSetAttr(counter, "description", gSessionData->mPerfCounterDescription[x]); } } - endElement("counters"); } - endElement("captured"); - return getXmlString(); + + return xml; +} + +char* CapturedXML::getXML() { + char* xml_string; + mxml_node_t *xml = getTree(); + xml_string = mxmlSaveAllocString(xml, mxmlWhitespaceCB); + mxmlDelete(xml); + return xml_string; } void CapturedXML::write(char* path) { - char* file = (char*)malloc(PATH_MAX); + char *file = (char*)malloc(PATH_MAX); // Set full path snprintf(file, PATH_MAX, "%s/captured.xml", path); - // Write the file - const char* xml = getXML(); + char* xml = getXML(); if (util->writeToDisk(file, xml) < 0) { logg->logError(__FILE__, __LINE__, "Error writing %s\nPlease verify the path.", file); handleException(); } + free(xml); free(file); } diff --git a/daemon/CapturedXML.h b/daemon/CapturedXML.h index d37c59e..984d1f2 100644 --- a/daemon/CapturedXML.h +++ b/daemon/CapturedXML.h @@ -10,14 +10,16 @@ #ifndef __CAPTURED_XML_H__ #define __CAPTURED_XML_H__ -#include "XMLOut.h" +#include "mxml/mxml.h" -class CapturedXML : XMLOut { +class CapturedXML { public: CapturedXML(); ~CapturedXML(); - const char* getXML(); + char* getXML(); // the string should be freed by the caller void write(char* path); +private: + mxml_node_t* getTree(); }; #endif //__CAPTURED_XML_H__ diff --git a/daemon/Child.cpp b/daemon/Child.cpp index ddf105b..bcc868a 100644 --- a/daemon/Child.cpp +++ b/daemon/Child.cpp @@ -134,6 +134,7 @@ void* stopThread(void* pVoid) { void* senderThread(void* pVoid) { int length; char* data; + char end_sequence[] = {RESPONSE_APC_DATA, 0, 0, 0, 0}; sem_post(&senderThreadStarted); prctl(PR_SET_NAME, (unsigned int)&"gatord-sender", 0, 0, 0); @@ -143,20 +144,25 @@ void* senderThread(void* pVoid) { data = collectorFifo->read(&length); sender->writeData(data, length, RESPONSE_APC_DATA); } while (length > 0); + + // write end-of-capture sequence + if (!gSessionData->mLocalCapture) { + sender->writeData(end_sequence, sizeof(end_sequence), RESPONSE_APC_DATA); + } + logg->logMessage("Exit sender thread"); return 0; } -Child::Child(char* path) { +Child::Child() { initialization(); - sessionXMLPath = path; gSessionData->mLocalCapture = true; } Child::Child(OlySocket* sock, int conn) { initialization(); socket = sock; - numConnections = conn; + mNumConnections = conn; } Child::~Child() { @@ -171,8 +177,7 @@ void Child::initialization() { signal(SIGALRM, child_handler); socket = NULL; numExceptions = 0; - numConnections = 0; - sessionXMLPath = 0; + mNumConnections = 0; // Initialize semaphores sem_init(&senderThreadStarted, 0, 0); @@ -189,13 +194,17 @@ void Child::run() { char* collectBuffer; int bytesCollected = 0; LocalCapture* localCapture = NULL; + pthread_t durationThreadID, stopThreadID, senderThreadID; prctl(PR_SET_NAME, (unsigned int)&"gatord-child", 0, 0, 0); + // Disable line wrapping when generating xml files; carriage returns and indentation to be added manually + mxmlSetWrapMargin(0); + // Instantiate the Sender - must be done first, after which error messages can be sent sender = new Sender(socket); - if (numConnections > 1) { + if (mNumConnections > 1) { logg->logError(__FILE__, __LINE__, "Session already in progress"); handleException(); } @@ -211,29 +220,27 @@ void Child::run() { // Respond to Streamline requests StreamlineSetup ss(socket); } else { - xmlString = util->readFromDisk(sessionXMLPath); + char* xmlString; + xmlString = util->readFromDisk(gSessionData->mSessionXMLPath); if (xmlString == 0) { - logg->logError(__FILE__, __LINE__, "Unable to read session xml file: %s", sessionXMLPath); + logg->logError(__FILE__, __LINE__, "Unable to read session xml file: %s", gSessionData->mSessionXMLPath); handleException(); } gSessionData->parseSessionXML(xmlString); localCapture = new LocalCapture(); - localCapture->createAPCDirectory(gSessionData->target_path, gSessionData->title); - localCapture->copyImages(gSessionData->images); + localCapture->createAPCDirectory(gSessionData->mTargetPath, gSessionData->mTitle); + localCapture->copyImages(gSessionData->mImages); localCapture->write(xmlString); - sender->createDataFile(gSessionData->apcDir); - delete xmlString; + sender->createDataFile(gSessionData->mAPCDir); + free(xmlString); } // Write configuration into the driver collector->setupPerfCounters(); - // Create user-space buffers - int fifoBufferSize = collector->getBufferSize(); - int numCollectorBuffers = (gSessionData->mTotalBufferSize * 1024 * 1024 + fifoBufferSize - 1) / fifoBufferSize; - numCollectorBuffers = (numCollectorBuffers < 4) ? 4 : numCollectorBuffers; - logg->logMessage("Created %d %d-byte collector buffers", numCollectorBuffers, fifoBufferSize); - collectorFifo = new Fifo(numCollectorBuffers, fifoBufferSize); + // Create user-space buffers, add 5 to the size to account for the 1-byte type and 4-byte length + logg->logMessage("Created %d MB collector buffer with a %d-byte ragged end", gSessionData->mTotalBufferSize, collector->getBufferSize()); + collectorFifo = new Fifo(collector->getBufferSize() + 5, gSessionData->mTotalBufferSize* 1024 * 1024); // Get the initial pointer to the collect buffer collectBuffer = collectorFifo->start(); @@ -243,12 +250,14 @@ void Child::run() { // Create the duration, stop, and sender threads bool thread_creation_success = true; - if (gSessionData->mDuration > 0 && pthread_create(&durationThreadID, NULL, durationThread, NULL)) + if (gSessionData->mDuration > 0 && pthread_create(&durationThreadID, NULL, durationThread, NULL)) { thread_creation_success = false; - else if (socket && pthread_create(&stopThreadID, NULL, stopThread, NULL)) + } else if (socket && pthread_create(&stopThreadID, NULL, stopThread, NULL)) { thread_creation_success = false; - else if (pthread_create(&senderThreadID, NULL, senderThread, NULL)) + } else if (pthread_create(&senderThreadID, NULL, senderThread, NULL)){ thread_creation_success = false; + } + if (!thread_creation_success) { logg->logError(__FILE__, __LINE__, "Failed to create gator threads"); handleException(); @@ -269,8 +278,7 @@ void Child::run() { // In one shot mode, stop collection once all the buffers are filled if (gSessionData->mOneShot && gSessionData->mSessionIsActive) { - // Depth minus 1 because write() has not yet been called - if ((bytesCollected == -1) || (collectorFifo->numWriteToReadBuffersFilled() == collectorFifo->depth() - 1)) { + if (bytesCollected == -1 || collectorFifo->willFill(bytesCollected)) { logg->logMessage("One shot"); endSession(); } @@ -292,7 +300,7 @@ void Child::run() { // Write the captured xml file if (gSessionData->mLocalCapture) { CapturedXML capturedXML; - capturedXML.write(gSessionData->apcDir); + capturedXML.write(gSessionData->mAPCDir); } logg->logMessage("Profiling ended."); diff --git a/daemon/Child.h b/daemon/Child.h index eee5ac7..612ca7c 100644 --- a/daemon/Child.h +++ b/daemon/Child.h @@ -15,7 +15,7 @@ class Child { public: - Child(char* sessionXMLPath); + Child(); Child(OlySocket* sock, int numConnections); ~Child(); void run(); @@ -23,11 +23,7 @@ public: void endSession(); int numExceptions; private: - char* xmlString; - char* sessionXMLPath; - int numConnections; - time_t timeStart; - pthread_t durationThreadID, stopThreadID, senderThreadID; + int mNumConnections; void initialization(); }; diff --git a/daemon/Collector.cpp b/daemon/Collector.cpp index 7a41198..db7b9c0 100644 --- a/daemon/Collector.cpp +++ b/daemon/Collector.cpp @@ -16,31 +16,32 @@ #include "Collector.h" #include "SessionData.h" #include "Logging.h" +#include "Sender.h" extern void handleException(); // Driver initialization independent of session settings Collector::Collector() { - char text[sizeof(gSessionData->mPerfCounterType[0]) + 20]; // sufficiently large to hold all events// + char text[sizeof(gSessionData->mPerfCounterType[0]) + 30]; // sufficiently large to hold all /dev/gator/events// - bufferFD = 0; + mBufferFD = 0; checkVersion(); int enable = -1; - if (readIntDriver("enable", &enable) != 0 || enable != 0) { + if (readIntDriver("/dev/gator/enable", &enable) != 0 || enable != 0) { logg->logError(__FILE__, __LINE__, "Driver already enabled, possibly a session is already in progress."); handleException(); } - readIntDriver("cpu_cores", &gSessionData->mCores); + readIntDriver("/dev/gator/cpu_cores", &gSessionData->mCores); if (gSessionData->mCores == 0) { gSessionData->mCores = 1; } - bufferSize = 512 * 1024; - if (writeReadDriver("buffer_size", &bufferSize) || bufferSize <= 0) { - logg->logError(__FILE__, __LINE__, "Unable to set the driver buffer size"); + mBufferSize = 0; + if (readIntDriver("/dev/gator/buffer_size", &mBufferSize) || mBufferSize <= 0) { + logg->logError(__FILE__, __LINE__, "Unable to read the driver buffer size"); handleException(); } @@ -51,7 +52,7 @@ Collector::Collector() { // Read unchanging keys from driver which are created at insmod'ing of gator.ko for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) { if (gSessionData->mPerfCounterEnabled[i]) { - snprintf(text, sizeof(text), "events/%s/key", gSessionData->mPerfCounterType[i]); + snprintf(text, sizeof(text), "/dev/gator/events/%s/key", gSessionData->mPerfCounterType[i]); readIntDriver(text, &gSessionData->mPerfCounterKey[i]); } } @@ -59,21 +60,37 @@ Collector::Collector() { Collector::~Collector() { // Write zero for safety, as a zero should have already been written - writeDriver("enable", "0"); + writeDriver("/dev/gator/enable", "0"); // Calls event_buffer_release in the driver - if (bufferFD) { - close(bufferFD); + if (mBufferFD) { + close(mBufferFD); } } +#include void Collector::enablePerfCounters() { char text[sizeof(gSessionData->mPerfCounterType[0]) + 30]; // sufficiently large to hold all /dev/gator/events//enabled + + // Initialize all perf counters in the driver, i.e. set enabled to zero + struct dirent *ent; + DIR* dir = opendir("/dev/gator/events"); + if (dir) { + while ((ent = readdir(dir)) != NULL) { + // skip hidden files, current dir, and parent dir + if (ent->d_name[0] == '.') + continue; + snprintf(text, sizeof(text), "/dev/gator/events/%s/enabled", ent->d_name); + writeDriver(text, 0); + } + closedir (dir); + } + for (int i=0; imPerfCounterEnabled[i]) { continue; } - snprintf(text, sizeof(text), "events/%s/enabled", gSessionData->mPerfCounterType[i]); + snprintf(text, sizeof(text), "/dev/gator/events/%s/enabled", gSessionData->mPerfCounterType[i]); if (writeReadDriver(text, &gSessionData->mPerfCounterEnabled[i])) { // Disable those events that don't exist on this hardware platform even though they exist in configuration.xml gSessionData->mPerfCounterEnabled[i] = 0; @@ -83,21 +100,21 @@ void Collector::enablePerfCounters() { } void Collector::setupPerfCounters() { - char base[sizeof(gSessionData->mPerfCounterType[0]) + 10]; // sufficiently large to hold all events/ - char text[sizeof(gSessionData->mPerfCounterType[0]) + 20]; // sufficiently large to hold all events// + char base[sizeof(gSessionData->mPerfCounterType[0]) + 20]; // sufficiently large to hold all /dev/gator/events/ + char text[sizeof(gSessionData->mPerfCounterType[0]) + 30]; // sufficiently large to hold all /dev/gator/events// for (int i=0; imPerfCounterEnabled[i]) { continue; } - snprintf(base, sizeof(base), "events/%s", gSessionData->mPerfCounterType[i]); + snprintf(base, sizeof(base), "/dev/gator/events/%s", gSessionData->mPerfCounterType[i]); snprintf(text, sizeof(text), "%s/event", base); writeDriver(text, gSessionData->mPerfCounterEvent[i]); if (gSessionData->mPerfCounterEBSCapable[i]) { snprintf(text, sizeof(text), "%s/count", base); - if (access(resolvePath(text), F_OK) == 0) { + if (access(text, F_OK) == 0) { if (writeReadDriver(text, &gSessionData->mPerfCounterCount[i]) && gSessionData->mPerfCounterCount[i] > 0) { - logg->logError(__FILE__, __LINE__, "Cannot enable EBS for %s with a count of %d\n", gSessionData->mPerfCounterName[i], gSessionData->mPerfCounterCount[i]); + logg->logError(__FILE__, __LINE__, "Cannot enable EBS for %s:%s with a count of %d\n", gSessionData->mPerfCounterTitle[i], gSessionData->mPerfCounterName[i], gSessionData->mPerfCounterCount[i]); handleException(); } } else if (gSessionData->mPerfCounterCount[i] > 0) { @@ -111,7 +128,7 @@ void Collector::setupPerfCounters() { void Collector::checkVersion() { int driver_version = 0; - if (readIntDriver("version", &driver_version) == -1) { + if (readIntDriver("/dev/gator/version", &driver_version) == -1) { logg->logError(__FILE__, __LINE__, "Error reading gator driver version"); handleException(); } @@ -139,61 +156,61 @@ void Collector::checkVersion() { void Collector::start() { // Set the maximum backtrace depth - if (writeReadDriver("backtrace_depth", &gSessionData->mBacktraceDepth)) { + if (writeReadDriver("/dev/gator/backtrace_depth", &gSessionData->mBacktraceDepth)) { logg->logError(__FILE__, __LINE__, "Unable to set the driver backtrace depth"); handleException(); } // open the buffer which calls userspace_buffer_open() in the driver - bufferFD = open(resolvePath("buffer"), O_RDONLY); - if (bufferFD < 0) { + mBufferFD = open("/dev/gator/buffer", O_RDONLY); + if (mBufferFD < 0) { logg->logError(__FILE__, __LINE__, "The gator driver did not set up properly. Please view the linux console or dmesg log for more information on the failure."); handleException(); } // set the tick rate of the profiling timer - if (writeReadDriver("tick", &gSessionData->mSampleRate) != 0) { + if (writeReadDriver("/dev/gator/tick", &gSessionData->mSampleRate) != 0) { logg->logError(__FILE__, __LINE__, "Unable to set the driver tick"); handleException(); } - // notify the kernel of the streaming mode, currently used for network stats - int streaming = (int)!gSessionData->mOneShot; - if (writeReadDriver("streaming", &streaming) != 0) { - logg->logError(__FILE__, __LINE__, "Unable to set streaming"); + // notify the kernel of the response type + int response_type = gSessionData->mLocalCapture ? 0 : RESPONSE_APC_DATA; + if (writeDriver("/dev/gator/response_type", response_type)) { + logg->logError(__FILE__, __LINE__, "Unable to write the response type"); handleException(); } logg->logMessage("Start the driver"); // This command makes the driver start profiling by calling gator_op_start() in the driver - if (writeDriver("enable", "1") != 0) { + if (writeDriver("/dev/gator/enable", "1") != 0) { logg->logError(__FILE__, __LINE__, "The gator driver did not start properly. Please view the linux console or dmesg log for more information on the failure."); handleException(); } - lseek(bufferFD, 0, SEEK_SET); + lseek(mBufferFD, 0, SEEK_SET); } // These commands should cause the read() function in collect() to return void Collector::stop() { // This will stop the driver from profiling - if (writeDriver("enable", "0") != 0) { + if (writeDriver("/dev/gator/enable", "0") != 0) { logg->logMessage("Stopping kernel failed"); } } int Collector::collect(char* buffer) { // Calls event_buffer_read in the driver - int bytesRead = read(bufferFD, buffer, bufferSize); + int bytesRead = read(mBufferFD, buffer, mBufferSize); // If read() returned due to an interrupt signal, re-read to obtain the last bit of collected data if (bytesRead == -1 && errno == EINTR) { - bytesRead = read(bufferFD, buffer, bufferSize); + bytesRead = read(mBufferFD, buffer, mBufferSize); } + // return the total bytes written logg->logMessage("Driver read of %d bytes", bytesRead); - return bytesRead; } @@ -209,8 +226,9 @@ void Collector::getCoreName() { } while (fgets(temp, sizeof(temp), f)) { - if (strlen(temp) > 0) + if (strlen(temp) > 0) { temp[strlen(temp) - 1] = 0; // Replace the line feed with a null + } if (strstr(temp, "Hardware") != 0) { char* position = strchr(temp, ':'); @@ -219,7 +237,7 @@ void Collector::getCoreName() { "The core name in the captured xml file will be 'unknown'."); return; } - strncpy(gSessionData->mCoreName, (char *)((int)position + 2), sizeof(gSessionData->mCoreName)); + strncpy(gSessionData->mCoreName, (char*)((int)position + 2), sizeof(gSessionData->mCoreName)); gSessionData->mCoreName[sizeof(gSessionData->mCoreName) - 1] = 0; // strncpy does not guarantee a null-terminated string fclose(f); return; @@ -231,14 +249,7 @@ void Collector::getCoreName() { fclose(f); } -char* Collector::resolvePath(const char* file) { - static char fullpath[100]; // Sufficiently large to hold any path within /dev/gator - snprintf(fullpath, sizeof(fullpath), "/dev/gator/%s", file); - return fullpath; -} - -int Collector::readIntDriver(const char* path, int* value) { - char* fullpath = resolvePath(path); +int Collector::readIntDriver(const char* fullpath, int* value) { FILE* file = fopen(fullpath, "r"); if (file == NULL) { return -1; @@ -258,8 +269,7 @@ int Collector::writeDriver(const char* path, int value) { return writeDriver(path, data); } -int Collector::writeDriver(const char* path, const char* data) { - char* fullpath = resolvePath(path); +int Collector::writeDriver(const char* fullpath, const char* data) { int fd = open(fullpath, O_WRONLY); if (fd < 0) { return -1; diff --git a/daemon/Collector.h b/daemon/Collector.h index 6c80725..5977a72 100644 --- a/daemon/Collector.h +++ b/daemon/Collector.h @@ -20,16 +20,14 @@ public: int collect(char* buffer); void enablePerfCounters(); void setupPerfCounters(); - int getBufferSize() {return bufferSize;} + int getBufferSize() {return mBufferSize;} private: - int bufferSize; - int bufferFD; - char * buffer; + int mBufferSize; + int mBufferFD; void checkVersion(); void getCoreName(); - char* resolvePath(const char* file); int readIntDriver(const char* path, int* value); int writeDriver(const char* path, int value); int writeDriver(const char* path, const char* data); diff --git a/daemon/ConfigurationXML.cpp b/daemon/ConfigurationXML.cpp index e1bec0f..145ddbd 100644 --- a/daemon/ConfigurationXML.cpp +++ b/daemon/ConfigurationXML.cpp @@ -17,7 +17,7 @@ extern void handleException(); static const char* ATTR_COUNTER = "counter"; -static const char* ATTR_VERSION = "version"; +static const char* ATTR_REVISION = "revision"; static const char* ATTR_TITLE = "title"; static const char* ATTR_NAME = "name"; static const char* ATTR_EVENT = "event"; @@ -27,14 +27,19 @@ static const char* ATTR_OPERATION = "operation"; static const char* ATTR_PER_CPU = "per_cpu"; static const char* ATTR_DESCRIPTION = "description"; static const char* ATTR_EBS = "event_based_sampling"; +static const char* ATTR_LEVEL = "level"; +static const char* ATTR_ALIAS = "alias"; +static const char* ATTR_DISPLAY = "display"; +static const char* ATTR_UNITS = "units"; +static const char* ATTR_AVERAGE_SELECTION = "average_selection"; ConfigurationXML::ConfigurationXML() { #include "configuration_xml.h" // defines and initializes char configuration_xml[] and int configuration_xml_len - index = 0; - char* path = (char *)malloc(PATH_MAX); + mIndex = 0; + char* path = (char*)malloc(PATH_MAX); - if (gSessionData->configurationXMLPath) { - strncpy(path, gSessionData->configurationXMLPath, PATH_MAX); + if (gSessionData->mConfigurationXMLPath) { + strncpy(path, gSessionData->mConfigurationXMLPath, PATH_MAX); } else { if (util->getApplicationFullPath(path, PATH_MAX) != 0) { logg->logMessage("Unable to determine the full path of gatord, the cwd will be used"); @@ -63,10 +68,10 @@ ConfigurationXML::ConfigurationXML() { logg->logError(__FILE__, __LINE__, "Invalid configuration.xml file detected and unable to delete it. To resolve, delete configuration.xml on disk"); handleException(); } - } else if (ret < 0 || isValid() == false) { - logg->logError(__FILE__, __LINE__, "Parsing of the configuration.xml file failed. Please verify configuration.xml on the target filesystem is valid or delete it to use the default."); - handleException(); + logg->logMessage("Invalid configuration.xml file detected and removed"); } + + validate(); free(path); } @@ -78,79 +83,106 @@ ConfigurationXML::~ConfigurationXML() { } int ConfigurationXML::parse(const char* configurationXML) { - int ret = 0; - XMLReader reader(configurationXML); - char * tag = reader.nextTag(); - while(tag != 0 && ret == 0) { - if (strcmp(tag, "configurations") == 0) { - ret = configurationsTag(&reader); - } else if (strcmp(tag, "configuration") == 0) { - ret = configurationTag(&reader); + mxml_node_t *tree, *node; + int ret; + + tree = mxmlLoadString(NULL, configurationXML, MXML_NO_CALLBACK); + + node = mxmlGetFirstChild(tree); + while (node && mxmlGetType(node) != MXML_ELEMENT) + node = mxmlWalkNext(node, tree, MXML_NO_DESCEND); + + ret = configurationsTag(node); + + node = mxmlGetFirstChild(node); + while (node) { + if (mxmlGetType(node) != MXML_ELEMENT) { + node = mxmlWalkNext(node, tree, MXML_NO_DESCEND); + continue; } - tag = reader.nextTag(); + configurationTag(node); + node = mxmlWalkNext(node, tree, MXML_NO_DESCEND); } + mxmlDelete(tree); + return ret; } -bool ConfigurationXML::isValid(void) { +void ConfigurationXML::validate(void) { for (int i = 0; i < MAX_PERFORMANCE_COUNTERS; i++) { if (gSessionData->mPerfCounterEnabled[i]) { - if (strcmp(gSessionData->mPerfCounterType[i], "") == 0 || - strcmp(gSessionData->mPerfCounterTitle[i], "") == 0 || - strcmp(gSessionData->mPerfCounterName[i], "") == 0) { - logg->logMessage("Invalid required attribute\n counter=\"%s\"\n title=\"%s\"\n name=\"%s\"\n event=%d\n", gSessionData->mPerfCounterType[i], gSessionData->mPerfCounterTitle[i], gSessionData->mPerfCounterName[i], gSessionData->mPerfCounterEvent[i]); - return false; // failure + if (strcmp(gSessionData->mPerfCounterType[i], "") == 0) { + logg->logError(__FILE__, __LINE__, "Invalid required attribute in configuration.xml:\n counter=\"%s\"\n title=\"%s\"\n name=\"%s\"\n event=%d\n", gSessionData->mPerfCounterType[i], gSessionData->mPerfCounterTitle[i], gSessionData->mPerfCounterName[i], gSessionData->mPerfCounterEvent[i]); + handleException(); } // iterate through the remaining enabled performance counters for (int j = i + 1; j < MAX_PERFORMANCE_COUNTERS; j++) { if (gSessionData->mPerfCounterEnabled[j]) { - // check if the type or device are the same + // check if the types are the same if (strcmp(gSessionData->mPerfCounterType[i], gSessionData->mPerfCounterType[j]) == 0) { - logg->logMessage("Duplicate performance counter type: %s", gSessionData->mPerfCounterType[i]); - return false; // failure + logg->logError(__FILE__, __LINE__, "Duplicate performance counter type in configuration.xml: %s", gSessionData->mPerfCounterType[i]); + handleException(); } } } } } - - return true; // success } -#define CONFIGURATION_VERSION 1 -int ConfigurationXML::configurationsTag(XMLReader *in) { - int version = in->getAttributeAsInteger(ATTR_VERSION, 0); - if (version != CONFIGURATION_VERSION) { - logg->logMessage("Incompatible configuration.xml version (%d) detected. The version needs to be %d.", version, CONFIGURATION_VERSION); - return 1; // version issue +#define CONFIGURATION_REVISION 1 +int ConfigurationXML::configurationsTag(mxml_node_t *node) { + const char* revision_string; + + revision_string = mxmlElementGetAttr(node, ATTR_REVISION); + if (!revision_string) { + return 1; //revision issue; + } + + int revision = strtol(revision_string, NULL, 10); + if (revision < CONFIGURATION_REVISION) { + return 1; // revision issue } + return 0; } -int ConfigurationXML::configurationTag(XMLReader* in) { +void ConfigurationXML::configurationTag(mxml_node_t *node) { // handle all other performance counters - if (index >= MAX_PERFORMANCE_COUNTERS) { - logg->logMessage("Invalid performance counter index: %d", index); - return -1; // failure + if (mIndex >= MAX_PERFORMANCE_COUNTERS) { + logg->logError(__FILE__, __LINE__, "Exceeded maximum number of %d performance counters", MAX_PERFORMANCE_COUNTERS); + handleException(); } // read attributes - in->getAttribute(ATTR_COUNTER, gSessionData->mPerfCounterType[index], sizeof(gSessionData->mPerfCounterType[index]), ""); - in->getAttribute(ATTR_TITLE, gSessionData->mPerfCounterTitle[index], sizeof(gSessionData->mPerfCounterTitle[index]), ""); - in->getAttribute(ATTR_NAME, gSessionData->mPerfCounterName[index], sizeof(gSessionData->mPerfCounterName[index]), ""); - in->getAttribute(ATTR_DESCRIPTION, gSessionData->mPerfCounterDescription[index], sizeof(gSessionData->mPerfCounterDescription[index]), ""); - gSessionData->mPerfCounterEvent[index] = in->getAttributeAsInteger(ATTR_EVENT, 0); - gSessionData->mPerfCounterCount[index] = in->getAttributeAsInteger(ATTR_COUNT, 0); - gSessionData->mPerfCounterColor[index] = in->getAttributeAsInteger(ATTR_COLOR, 0); - gSessionData->mPerfCounterPerCPU[index] = in->getAttributeAsBoolean(ATTR_PER_CPU, false); - gSessionData->mPerfCounterEBSCapable[index] = in->getAttributeAsBoolean(ATTR_EBS, false); - in->getAttribute(ATTR_OPERATION, gSessionData->mPerfCounterOperation[index], sizeof(gSessionData->mPerfCounterOperation[index]), ""); - gSessionData->mPerfCounterEnabled[index] = true; + if (mxmlElementGetAttr(node, ATTR_COUNTER)) strncpy(gSessionData->mPerfCounterType[mIndex], mxmlElementGetAttr(node, ATTR_COUNTER), sizeof(gSessionData->mPerfCounterType[mIndex])); + if (mxmlElementGetAttr(node, ATTR_TITLE)) strncpy(gSessionData->mPerfCounterTitle[mIndex], mxmlElementGetAttr(node, ATTR_TITLE), sizeof(gSessionData->mPerfCounterTitle[mIndex])); + if (mxmlElementGetAttr(node, ATTR_NAME)) strncpy(gSessionData->mPerfCounterName[mIndex], mxmlElementGetAttr(node, ATTR_NAME), sizeof(gSessionData->mPerfCounterName[mIndex])); + if (mxmlElementGetAttr(node, ATTR_DESCRIPTION)) strncpy(gSessionData->mPerfCounterDescription[mIndex], mxmlElementGetAttr(node, ATTR_DESCRIPTION), sizeof(gSessionData->mPerfCounterDescription[mIndex])); + if (mxmlElementGetAttr(node, ATTR_EVENT)) gSessionData->mPerfCounterEvent[mIndex] = strtol(mxmlElementGetAttr(node, ATTR_EVENT), NULL, 16); + if (mxmlElementGetAttr(node, ATTR_COUNT)) gSessionData->mPerfCounterCount[mIndex] = strtol(mxmlElementGetAttr(node, ATTR_COUNT), NULL, 10); + if (mxmlElementGetAttr(node, ATTR_COLOR)) gSessionData->mPerfCounterColor[mIndex] = strtol(mxmlElementGetAttr(node, ATTR_COLOR), NULL, 16); + if (mxmlElementGetAttr(node, ATTR_PER_CPU)) gSessionData->mPerfCounterPerCPU[mIndex] = util->stringToBool(mxmlElementGetAttr(node, ATTR_PER_CPU), false); + if (mxmlElementGetAttr(node, ATTR_EBS)) gSessionData->mPerfCounterEBSCapable[mIndex] = util->stringToBool(mxmlElementGetAttr(node, ATTR_EBS), false); + if (mxmlElementGetAttr(node, ATTR_OPERATION)) strncpy(gSessionData->mPerfCounterOperation[mIndex], mxmlElementGetAttr(node, ATTR_OPERATION), sizeof(gSessionData->mPerfCounterOperation[mIndex])); + if (mxmlElementGetAttr(node, ATTR_LEVEL)) gSessionData->mPerfCounterLevel[mIndex] = util->stringToBool(mxmlElementGetAttr(node, ATTR_LEVEL), false); + if (mxmlElementGetAttr(node, ATTR_ALIAS)) strncpy(gSessionData->mPerfCounterAlias[mIndex], mxmlElementGetAttr(node, ATTR_ALIAS), sizeof(gSessionData->mPerfCounterAlias[mIndex])); + if (mxmlElementGetAttr(node, ATTR_DISPLAY)) strncpy(gSessionData->mPerfCounterDisplay[mIndex], mxmlElementGetAttr(node, ATTR_DISPLAY), sizeof(gSessionData->mPerfCounterDisplay[mIndex])); + if (mxmlElementGetAttr(node, ATTR_UNITS)) strncpy(gSessionData->mPerfCounterUnits[mIndex], mxmlElementGetAttr(node, ATTR_UNITS), sizeof(gSessionData->mPerfCounterUnits[mIndex])); + if (mxmlElementGetAttr(node, ATTR_AVERAGE_SELECTION)) gSessionData->mPerfCounterAverageSelection[mIndex] = util->stringToBool(mxmlElementGetAttr(node, ATTR_AVERAGE_SELECTION), false); + gSessionData->mPerfCounterEnabled[mIndex] = true; + + // strncpy does not guarantee a null-termianted string + gSessionData->mPerfCounterType[mIndex][sizeof(gSessionData->mPerfCounterType[mIndex]) - 1] = 0; + gSessionData->mPerfCounterTitle[mIndex][sizeof(gSessionData->mPerfCounterTitle[mIndex]) - 1] = 0; + gSessionData->mPerfCounterName[mIndex][sizeof(gSessionData->mPerfCounterName[mIndex]) - 1] = 0; + gSessionData->mPerfCounterDescription[mIndex][sizeof(gSessionData->mPerfCounterDescription[mIndex]) - 1] = 0; + gSessionData->mPerfCounterOperation[mIndex][sizeof(gSessionData->mPerfCounterOperation[mIndex]) - 1] = 0; + gSessionData->mPerfCounterAlias[mIndex][sizeof(gSessionData->mPerfCounterAlias[mIndex]) - 1] = 0; + gSessionData->mPerfCounterDisplay[mIndex][sizeof(gSessionData->mPerfCounterDisplay[mIndex]) - 1] = 0; + gSessionData->mPerfCounterUnits[mIndex][sizeof(gSessionData->mPerfCounterUnits[mIndex]) - 1] = 0; // update counter index - index++; - - return 0; // success + mIndex++; } diff --git a/daemon/ConfigurationXML.h b/daemon/ConfigurationXML.h index 047c4b0..b48d32f 100644 --- a/daemon/ConfigurationXML.h +++ b/daemon/ConfigurationXML.h @@ -9,21 +9,21 @@ #ifndef COUNTERS_H #define COUNTERS_H -#include "XMLReader.h" +#include "mxml/mxml.h" class ConfigurationXML { public: ConfigurationXML(); ~ConfigurationXML(); const char* getConfigurationXML() {return mConfigurationXML;} + void validate(void); private: char* mConfigurationXML; + int mIndex; int parse(const char* xmlFile); - bool isValid(void); - int configurationsTag(XMLReader *in); - int configurationTag(XMLReader* in); - int index; + int configurationsTag(mxml_node_t *node); + void configurationTag(mxml_node_t *node); }; #endif // COUNTERS_H diff --git a/daemon/Fifo.cpp b/daemon/Fifo.cpp index 1456183..bbfc919 100644 --- a/daemon/Fifo.cpp +++ b/daemon/Fifo.cpp @@ -1,103 +1,126 @@ -/** - * Copyright (C) ARM Limited 2010-2012. 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 -#include -#include -#include "Fifo.h" -#include "Logging.h" - -extern void handleException(); - -Fifo::Fifo(int numBuffers, int bufferSize) { - int which; - - if (numBuffers > FIFO_BUFFER_LIMIT) { - logg->logError(__FILE__, __LINE__, "Number of fifo buffers exceeds maximum"); - handleException(); - } - mNumBuffers = numBuffers; - mBufferSize = bufferSize; - mWriteCurrent = 0; - mReadCurrent = mNumBuffers - 1; // (n-1) pipelined - - for (which=0; whichlogError(__FILE__, __LINE__, "sem_init(%d) failed", which); - handleException(); - } - // page-align allocate buffers - mBuffer[which] = (char*)valloc(bufferSize); - if (mBuffer[which] == NULL) { - logg->logError(__FILE__, __LINE__, "failed to allocate %d bytes", bufferSize); - handleException(); - } - // touch each page to fault it in - for (int i=0; i +#include +#include +#include "Fifo.h" +#include "Logging.h" + +extern void handleException(); + +// bufferSize is the amount of data to be filled +// singleBufferSize is the maximum size that may be filled during a single write +// (bufferSize + singleBufferSize) will be allocated +Fifo::Fifo(int singleBufferSize, int bufferSize) { + mWrite = mRead = mReadCommit = mRaggedEnd = 0; + mWrapThreshold = bufferSize; + mSingleBufferSize = singleBufferSize; + mBuffer = (char*)valloc(bufferSize + singleBufferSize); + mEnd = false; + + if (mBuffer == NULL) { + logg->logError(__FILE__, __LINE__, "failed to allocate %d bytes", bufferSize + singleBufferSize); + handleException(); + } + + if (sem_init(&mWaitForSpaceSem, 0, 0) || sem_init(&mWaitForDataSem, 0, 0)) { + logg->logError(__FILE__, __LINE__, "sem_init() failed"); + handleException(); + } +} + +Fifo::~Fifo() { + free(mBuffer); +} + +int Fifo::numBytesFilled() { + return mWrite - mRead + mRaggedEnd; +} + +char* Fifo::start() { + return mBuffer; +} + +bool Fifo::isEmpty() { + return mRead == mWrite; +} + +bool Fifo::isFull() { + return willFill(0); +} + +// Determines if the buffer will fill assuming 'additional' bytes will be added to the buffer +// comparisons use '<', read and write pointers must never equal when not empty +// 'full' means there is less than singleBufferSize bytes available; it does not mean there are zero bytes available +bool Fifo::willFill(int additional) { + if (mWrite > mRead) { + if (numBytesFilled() + additional < mWrapThreshold) { + return false; + } + } else { + if (numBytesFilled() + additional < mWrapThreshold - mSingleBufferSize) { + return false; + } + } + return true; +} + +// This function will stall until contiguous singleBufferSize bytes are available +char* Fifo::write(int length) { + if (length <= 0) { + length = 0; + mEnd = true; + } + + // update the write pointer + mWrite += length; + + // handle the wrap-around + if (mWrite >= mWrapThreshold) { + mRaggedEnd = mWrite; + mWrite = 0; + } + + // send a notification that data is ready + sem_post(&mWaitForDataSem); + + // wait for space + while (isFull()) { + sem_wait(&mWaitForSpaceSem); + } + + return &mBuffer[mWrite]; +} + +// This function will stall until data is available +char* Fifo::read(int* length) { + // update the read pointer now that the data has been handled + mRead = mReadCommit; + + // handle the wrap-around + if (mRead >= mWrapThreshold) { + mRaggedEnd = mRead = mReadCommit = 0; + } + + // send a notification that data is free (space is available) + sem_post(&mWaitForSpaceSem); + + // wait for data + while (isEmpty() && !mEnd) { + sem_wait(&mWaitForDataSem); + } + + // obtain the length + do { + mReadCommit = mRaggedEnd ? mRaggedEnd : mWrite; + *length = mReadCommit - mRead; + } while (*length < 0); // plugs race condition without using semaphores + + return &mBuffer[mRead]; +} diff --git a/daemon/Fifo.h b/daemon/Fifo.h index 51e2bcd..548ba27 100644 --- a/daemon/Fifo.h +++ b/daemon/Fifo.h @@ -1,41 +1,33 @@ -/** - * Copyright (C) ARM Limited 2010-2012. 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. - */ - -#ifndef __FIFO_H__ -#define __FIFO_H__ - -#include - -// Number of buffers allowed with large buffer mode -#define FIFO_BUFFER_LIMIT 64 - -class Fifo { -public: - Fifo(int numBuffers, int bufferSize); - ~Fifo(); - int depth(void); - int numReadToWriteBuffersFilled(); - int numWriteToReadBuffersFilled(); - int numReadToWriteBuffersEmpty() {return depth() - numReadToWriteBuffersFilled();} - int numWriteToReadBuffersEmpty() {return depth() - numWriteToReadBuffersFilled();} - char* start(); - char* write(int length); - char* read(int* length); - -private: - int mNumBuffers; - int mBufferSize; - int mWriteCurrent; - int mReadCurrent; - sem_t mReadToWriteSem[FIFO_BUFFER_LIMIT]; - sem_t mWriteToReadSem[FIFO_BUFFER_LIMIT]; - char* mBuffer[FIFO_BUFFER_LIMIT]; - int mLength[FIFO_BUFFER_LIMIT]; -}; - -#endif //__FIFO_H__ +/** + * Copyright (C) ARM Limited 2010-2012. 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. + */ + +#ifndef __FIFO_H__ +#define __FIFO_H__ + +#include + +class Fifo { +public: + Fifo(int singleBufferSize, int totalBufferSize); + ~Fifo(); + int numBytesFilled(); + bool isEmpty(); + bool isFull(); + bool willFill(int additional); + char* start(); + char* write(int length); + char* read(int* length); + +private: + int mSingleBufferSize, mWrite, mRead, mReadCommit, mRaggedEnd, mWrapThreshold; + sem_t mWaitForSpaceSem, mWaitForDataSem; + char* mBuffer; + bool mEnd; +}; + +#endif //__FIFO_H__ diff --git a/daemon/LocalCapture.cpp b/daemon/LocalCapture.cpp index 4a2d6d7..6449d03 100644 --- a/daemon/LocalCapture.cpp +++ b/daemon/LocalCapture.cpp @@ -24,9 +24,9 @@ LocalCapture::LocalCapture() {} LocalCapture::~LocalCapture() {} void LocalCapture::createAPCDirectory(char* target_path, char* name) { - gSessionData->apcDir = createUniqueDirectory(target_path, ".apc", name); - if ((removeDirAndAllContents(gSessionData->apcDir) != 0 || mkdir(gSessionData->apcDir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0)) { - logg->logError(__FILE__, __LINE__, "Unable to create directory %s", gSessionData->apcDir); + gSessionData->mAPCDir = createUniqueDirectory(target_path, ".apc", name); + if ((removeDirAndAllContents(gSessionData->mAPCDir) != 0 || mkdir(gSessionData->mAPCDir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0)) { + logg->logError(__FILE__, __LINE__, "Unable to create directory %s", gSessionData->mAPCDir); handleException(); } } @@ -35,7 +35,7 @@ void LocalCapture::write(char* string) { char* file = (char*)malloc(PATH_MAX); // Set full path - snprintf(file, PATH_MAX, "%s/session.xml", gSessionData->apcDir); + snprintf(file, PATH_MAX, "%s/session.xml", gSessionData->mAPCDir); // Write the file if (util->writeToDisk(file, string) < 0) { @@ -53,12 +53,14 @@ char* LocalCapture::createUniqueDirectory(const char* initialPath, const char* e // Ensure the path is an absolute path, i.e. starts with a slash if (initialPath == 0 || strlen(initialPath) == 0) { - if (getcwd(path, PATH_MAX) == 0) + if (getcwd(path, PATH_MAX) == 0) { logg->logMessage("Unable to retrive the current working directory"); + } strncat(path, "/@F_@N", PATH_MAX - strlen(path) - 1); } else if (initialPath[0] != '/') { - if (getcwd(path, PATH_MAX) == 0) + if (getcwd(path, PATH_MAX) == 0) { logg->logMessage("Unable to retrive the current working directory"); + } strncat(path, "/", PATH_MAX - strlen(path) - 1); strncat(path, initialPath, PATH_MAX - strlen(path) - 1); } else { @@ -158,7 +160,7 @@ void LocalCapture::replaceAll(char* target, const char* find, const char* replac free(original); } -int LocalCapture::removeDirAndAllContents(char *path) { +int LocalCapture::removeDirAndAllContents(char* path) { int error = 0; struct stat mFileInfo; // Does the path exist? @@ -173,7 +175,9 @@ int LocalCapture::removeDirAndAllContents(char *path) { sprintf(newpath, "%s/%s", path, entry->d_name); error = removeDirAndAllContents(newpath); free(newpath); - if (error) break; + if (error) { + break; + } } entry = readdir(dir); } @@ -192,15 +196,17 @@ void LocalCapture::copyImages(ImageLinkList* ptr) { char* dstfilename = (char*)malloc(PATH_MAX); while (ptr) { - strncpy(dstfilename, gSessionData->apcDir, PATH_MAX); + strncpy(dstfilename, gSessionData->mAPCDir, PATH_MAX); dstfilename[PATH_MAX - 1] = 0; // strncpy does not guarantee a null-terminated string - if (gSessionData->apcDir[strlen(gSessionData->apcDir) - 1] != '/') + if (gSessionData->mAPCDir[strlen(gSessionData->mAPCDir) - 1] != '/') { strncat(dstfilename, "/", PATH_MAX - strlen(dstfilename) - 1); + } strncat(dstfilename, util->getFilePart(ptr->path), PATH_MAX - strlen(dstfilename) - 1); - if (util->copyFile(ptr->path, dstfilename)) + if (util->copyFile(ptr->path, dstfilename)) { logg->logMessage("copied file %s to %s", ptr->path, dstfilename); - else + } else { logg->logMessage("copy of file %s to %s failed", ptr->path, dstfilename); + } ptr = ptr->next; } diff --git a/daemon/LocalCapture.h b/daemon/LocalCapture.h index 4f5f818..ca37f6e 100644 --- a/daemon/LocalCapture.h +++ b/daemon/LocalCapture.h @@ -15,13 +15,13 @@ class LocalCapture { public: LocalCapture(); ~LocalCapture(); - void write(char *string); + void write(char* string); void copyImages(ImageLinkList* ptr); void createAPCDirectory(char* target_path, char* name); private: char* createUniqueDirectory(const char* path, const char* ending, char* title); void replaceAll(char* target, const char* find, const char* replace, unsigned int size); - int removeDirAndAllContents(char *path); + int removeDirAndAllContents(char* path); }; #endif //__LOCAL_CAPTURE_H__ diff --git a/daemon/Makefile b/daemon/Makefile index d73d95b..aa1c005 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -7,6 +7,7 @@ ARCH=arm CPP=$(CROSS_COMPILE)g++ +GCC=$(CROSS_COMPILE)gcc # -g produces debugging information # -O3 maximum optimization @@ -17,13 +18,18 @@ CPP=$(CROSS_COMPILE)g++ # -std=c++98 is the 1998 c++ standard # -march=armv5t is required to set the minimum architecture # -mthumb-interwork is required for interworking to ARM or Thumb stdlibc -CFLAGS=-O3 -Wall -Werror +CFLAGS=-O3 -Wall -Werror -march=armv5t -mthumb-interwork TARGET=gatord -CPP_SRC = $(wildcard *.cpp) -TGT_OBJS = $(CPP_SRC:%.cpp=%.o) +C_SRC = $(wildcard mxml/*.c) +CPP_SRC = $(wildcard *.cpp) +TGT_OBJS = $(CPP_SRC:%.cpp=%.o) \ + $(C_SRC:%.c=%.o) all: $(TARGET) +%.o: %.c *.h + $(GCC) -c $(CFLAGS) -o $@ $< + %.o: %.cpp *.h $(CPP) -c $(CFLAGS) -o $@ $< @@ -37,4 +43,4 @@ convert: xxd -i configuration.xml > configuration_xml.h clean: - rm -f *.o $(TARGET) events.xml events_xml.h configuration_xml.h + rm -f *.o mxml/*.o $(TARGET) events.xml events_xml.h configuration_xml.h diff --git a/daemon/OlySocket.cpp b/daemon/OlySocket.cpp old mode 100644 new mode 100755 index 8a9ca97..a3bf746 --- a/daemon/OlySocket.cpp +++ b/daemon/OlySocket.cpp @@ -46,7 +46,7 @@ OlySocket::OlySocket(int port, bool multiple) { } OlySocket::OlySocket(int port, char* host) { - fdServer = 0; + mFDServer = 0; createClientSocket(host, port); } @@ -70,11 +70,11 @@ void OlySocket::closeSocket() { } void OlySocket::closeServerSocket() { - if (CLOSE_SOCKET(fdServer) != 0) { + if (CLOSE_SOCKET(mFDServer) != 0) { logg->logError(__FILE__, __LINE__, "Failed to close server socket."); handleException(); } - fdServer = 0; + mFDServer = 0; } void OlySocket::createClientSocket(char* hostname, int portno) { @@ -95,8 +95,9 @@ void OlySocket::createClientSocket(char* hostname, int portno) { handleException(); } for (res=res0; res!=NULL; res = res->ai_next) { - if ( res->ai_family != PF_INET || res->ai_socktype != SOCK_STREAM ) + if ( res->ai_family != PF_INET || res->ai_socktype != SOCK_STREAM ) { continue; + } mSocketID = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (mSocketID < 0) { continue; @@ -105,7 +106,9 @@ void OlySocket::createClientSocket(char* hostname, int portno) { close(mSocketID); mSocketID = -1; } - if (mSocketID > 0) break; + if (mSocketID > 0) { + break; + } } freeaddrinfo(res0); if (mSocketID <= 0) { @@ -124,15 +127,15 @@ void OlySocket::createSingleServerConnection(int port) { void OlySocket::createServerSocket(int port) { // Create socket - fdServer = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); - if (fdServer < 0) { + mFDServer = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (mFDServer < 0) { logg->logError(__FILE__, __LINE__, "Error creating server socket"); handleException(); } // Enable address reuse, another solution would be to create the server socket once and only close it when the object exits int on = 1; - if (setsockopt(fdServer, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on)) != 0) { + if (setsockopt(mFDServer, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)) != 0) { logg->logError(__FILE__, __LINE__, "Setting server socket options failed"); handleException(); } @@ -145,13 +148,13 @@ void OlySocket::createServerSocket(int port) { sockaddr.sin_addr.s_addr = INADDR_ANY; // Bind the socket to an address - if (bind(fdServer, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) { + if (bind(mFDServer, (const struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) { logg->logError(__FILE__, __LINE__, "Binding of server socket failed.\nIs an instance already running?"); handleException(); } // Listen for connections on this socket - if (listen(fdServer, 1) < 0) { + if (listen(mFDServer, 1) < 0) { logg->logError(__FILE__, __LINE__, "Listening of server socket failed"); handleException(); } @@ -160,13 +163,13 @@ void OlySocket::createServerSocket(int port) { // mSocketID is always set to the most recently accepted connection // The user of this class should maintain the different socket connections, e.g. by forking the process int OlySocket::acceptConnection() { - if (fdServer <= 0) { + if (mFDServer <= 0) { logg->logError(__FILE__, __LINE__, "Attempting multiple connections on a single connection server socket or attempting to accept on a client socket"); handleException(); } // Accept a connection, note that this call blocks until a client connects - mSocketID = accept(fdServer, NULL, NULL); + mSocketID = accept(mFDServer, NULL, NULL); if (mSocketID < 0) { logg->logError(__FILE__, __LINE__, "Socket acceptance failed"); handleException(); @@ -230,8 +233,9 @@ int OlySocket::receiveString(char* buffer, int size) { int bytes_received = 0; bool found = false; - if (buffer == 0) + if (buffer == 0) { return 0; + } while (!found && bytes_received < size) { // Receive a single character diff --git a/daemon/OlySocket.h b/daemon/OlySocket.h old mode 100644 new mode 100755 index 2fe6e59..266a831 --- a/daemon/OlySocket.h +++ b/daemon/OlySocket.h @@ -25,11 +25,9 @@ public: int receive(char* buffer, int size); int receiveNBytes(char* buffer, int size); int receiveString(char* buffer, int size); - char* getLastError() {return strError;} int getSocketID() {return mSocketID;} private: - char* strError; - int mSocketID, fdServer; + int mSocketID, mFDServer; void createClientSocket(char* hostname, int port); void createSingleServerConnection(int port); void createServerSocket(int port); diff --git a/daemon/OlyUtility.cpp b/daemon/OlyUtility.cpp old mode 100644 new mode 100755 index 1b15d33..ec852df --- a/daemon/OlyUtility.cpp +++ b/daemon/OlyUtility.cpp @@ -21,9 +21,35 @@ OlyUtility* util = NULL; +bool OlyUtility::stringToBool(const char* string, bool defValue) { + char value[32]; + + strncpy(value, string, sizeof(value)); + if (value[0] == 0) { + return defValue; + } + value[sizeof(value) - 1] = 0; // strncpy does not guarantee a null-terminated string + + // Convert to lowercase + int i = 0; + while (value[i]) { + value[i] = tolower(value[i]); + i++; + } + + if (strcmp(value, "true") == 0 || strcmp(value, "yes") == 0 || strcmp(value, "1") == 0 || strcmp(value, "on") == 0) { + return true; + } else if (strcmp(value, "false") == 0 || strcmp(value, "no") == 0 || strcmp(value, "0") == 0 || strcmp(value, "off") == 0) { + return false; + } else { + return defValue; + } +} + void OlyUtility::stringToLower(char* string) { - if (string == NULL) + if (string == NULL) { return; + } while (*string) { *string = tolower(*string); @@ -40,8 +66,9 @@ int OlyUtility::getApplicationFullPath(char* fullpath, int sizeOfPath) { int length = readlink("/proc/self/exe", fullpath, sizeOfPath); #endif - if (length == sizeOfPath) + if (length == sizeOfPath) { return -1; + } fullpath[length] = 0; fullpath = getPathPart(fullpath); @@ -52,7 +79,9 @@ int OlyUtility::getApplicationFullPath(char* fullpath, int sizeOfPath) { char* OlyUtility::readFromDisk(const char* file, unsigned int *size, bool appendNull) { // Open the file FILE* pFile = fopen(file, "rb"); - if (pFile==NULL) return NULL; + if (pFile==NULL) { + return NULL; + } // Obtain file size fseek(pFile , 0 , SEEK_END); @@ -61,19 +90,28 @@ char* OlyUtility::readFromDisk(const char* file, unsigned int *size, bool append // Allocate memory to contain the whole file char* buffer = (char*)malloc(lSize + (int)appendNull); - if (buffer == NULL) return NULL; + if (buffer == NULL) { + fclose(pFile); + return NULL; + } // Copy the file into the buffer - if (fread(buffer, 1, lSize, pFile) != lSize) return NULL; + if (fread(buffer, 1, lSize, pFile) != lSize) { + free(buffer); + fclose(pFile); + return NULL; + } // Terminate fclose(pFile); - if (appendNull) + if (appendNull) { buffer[lSize] = 0; + } - if (size) + if (size) { *size = lSize; + } return buffer; } @@ -81,10 +119,15 @@ char* OlyUtility::readFromDisk(const char* file, unsigned int *size, bool append int OlyUtility::writeToDisk(const char* path, const char* data) { // Open the file FILE* pFile = fopen(path, "wb"); - if (pFile == NULL) return -1; + if (pFile == NULL) { + return -1; + } // Write the data to disk - if (fwrite(data, 1, strlen(data), pFile) != strlen(data)) return -1; + if (fwrite(data, 1, strlen(data), pFile) != strlen(data)) { + fclose(pFile); + return -1; + } // Terminate fclose(pFile); @@ -94,10 +137,15 @@ int OlyUtility::writeToDisk(const char* path, const char* data) { int OlyUtility::appendToDisk(const char* path, const char* data) { // Open the file FILE* pFile = fopen(path, "a"); - if (pFile == NULL) return -1; + if (pFile == NULL) { + return -1; + } // Write the data to disk - if (fwrite(data, 1, strlen(data), pFile) != strlen(data)) return -1; + if (fwrite(data, 1, strlen(data), pFile) != strlen(data)) { + fclose(pFile); + return -1; + } // Terminate fclose(pFile); @@ -110,7 +158,7 @@ int OlyUtility::appendToDisk(const char* path, const char* data) { * 0 is returned on an error; otherwise 1. */ #define TRANSFER_SIZE 1024 -int OlyUtility::copyFile(const char * srcFile, const char * dstFile) { +int OlyUtility::copyFile(const char* srcFile, const char* dstFile) { char* buffer = (char*)malloc(TRANSFER_SIZE); FILE * f_src = fopen(srcFile,"rb"); if (!f_src) { @@ -165,3 +213,42 @@ char* OlyUtility::getPathPart(char* path) { return (path); } + +// whitespace callback utility function used with mini-xml +const char * mxmlWhitespaceCB(mxml_node_t *node, int loc) { + const char *name; + + name = mxmlGetElement(node); + + if (loc == MXML_WS_BEFORE_OPEN) { + // Single indentation + if (!strcmp(name, "target") || !strcmp(name, "counters")) + return("\n "); + + // Double indentation + if (!strcmp(name, "counter")) + return("\n "); + + // Avoid a carriage return on the first line of the xml file + if (!strncmp(name, "?xml", 4)) + return(NULL); + + // Default - no indentation + return("\n"); + } + + if (loc == MXML_WS_BEFORE_CLOSE) { + // No indentation + if (!strcmp(name, "captured")) + return("\n"); + + // Single indentation + if (!strcmp(name, "counters")) + return("\n "); + + // Default - no carriage return + return(NULL); + } + + return(NULL); +} diff --git a/daemon/OlyUtility.h b/daemon/OlyUtility.h old mode 100644 new mode 100755 index 0fa021d..793a733 --- a/daemon/OlyUtility.h +++ b/daemon/OlyUtility.h @@ -19,17 +19,20 @@ class OlyUtility { public: OlyUtility() {}; ~OlyUtility() {}; + bool stringToBool(const char* string, bool defValue); void stringToLower(char* string); int getApplicationFullPath(char* path, int sizeOfPath); char* readFromDisk(const char* file, unsigned int *size = NULL, bool appendNull = true); int writeToDisk(const char* path, const char* file); int appendToDisk(const char* path, const char* file); - int copyFile(const char * srcFile, const char * dstFile); + int copyFile(const char* srcFile, const char* dstFile); const char* getFilePart(const char* path); char* getPathPart(char* path); private: }; +#include "mxml/mxml.h" +const char * mxmlWhitespaceCB(mxml_node_t *node, int where); extern OlyUtility* util; #endif // OLY_UTILITY_H diff --git a/daemon/RequestXML.cpp b/daemon/RequestXML.cpp deleted file mode 100644 index e8f24d2..0000000 --- a/daemon/RequestXML.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (C) ARM Limited 2011-2012. 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 -#include -#include -#include "RequestXML.h" -#include "Logging.h" - -extern void handleException(); - -static const char* TAG_REQUEST = "request"; - -static const char* ATTR_PROTOCOL = "protocol"; -static const char* ATTR_EVENTS = "events"; -static const char* ATTR_CONFIGURATION = "configuration"; -static const char* ATTR_COUNTERS = "counters"; -static const char* ATTR_SESSION = "session"; -static const char* ATTR_CAPTURED = "captured"; -static const char* ATTR_DEFAULTS = "defaults"; - -RequestXML::RequestXML(const char * str) { - parameters.protocol = false; - parameters.events = false; - parameters.configuration = false; - parameters.counters = false; - parameters.session = false; - parameters.captured = false; - parameters.defaults = false; - - XMLReader reader(str); - char * tag = reader.nextTag(); - while(tag != 0) { - if (strcmp(tag, TAG_REQUEST) == 0) { - requestTag(&reader); - return; - } - tag = reader.nextTag(); - } - - logg->logError(__FILE__, __LINE__, "No request tag found in the request.xml file"); - handleException(); -} - -RequestXML::~RequestXML() { -} - -void RequestXML::requestTag(XMLReader* in) { - parameters.protocol = in->getAttributeAsBoolean(ATTR_PROTOCOL, false); - parameters.events = in->getAttributeAsBoolean(ATTR_EVENTS, false); - parameters.configuration = in->getAttributeAsBoolean(ATTR_CONFIGURATION, false); - parameters.counters = in->getAttributeAsBoolean(ATTR_COUNTERS, false); - parameters.session = in->getAttributeAsBoolean(ATTR_SESSION, false); - parameters.captured = in->getAttributeAsBoolean(ATTR_CAPTURED, false); - parameters.defaults = in->getAttributeAsBoolean(ATTR_DEFAULTS, false); -} diff --git a/daemon/RequestXML.h b/daemon/RequestXML.h deleted file mode 100644 index bebd048..0000000 --- a/daemon/RequestXML.h +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (C) ARM Limited 2010-2012. 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. - */ - -#ifndef REQUEST_XML_H -#define REQUEST_XML_H - -#include "XMLReader.h" - -struct ConfigParameters { - bool protocol; - bool events; - bool configuration; - bool counters; - bool session; - bool captured; - bool defaults; -}; - -class RequestXML { -public: - RequestXML(const char * str); - ~RequestXML(); - ConfigParameters parameters; -private: - void requestTag(XMLReader* in); -}; - -#endif // REQUEST_XML_H diff --git a/daemon/Sender.cpp b/daemon/Sender.cpp index 9792c36..efff753 100644 --- a/daemon/Sender.cpp +++ b/daemon/Sender.cpp @@ -20,18 +20,18 @@ extern void handleException(); Sender::Sender(OlySocket* socket) { - dataFile = NULL; - dataSocket = NULL; + mDataFile = NULL; + mDataSocket = NULL; // Set up the socket connection if (socket) { char streamline[64] = {0}; - dataSocket = socket; + mDataSocket = socket; // Receive magic sequence - can wait forever // Streamline will send data prior to the magic sequence for legacy support, which should be ignored for v4+ while (strcmp("STREAMLINE", streamline) != 0) { - if (dataSocket->receiveString(streamline, sizeof(streamline)) == -1) { + if (mDataSocket->receiveString(streamline, sizeof(streamline)) == -1) { logg->logError(__FILE__, __LINE__, "Socket disconnected"); handleException(); } @@ -39,32 +39,33 @@ Sender::Sender(OlySocket* socket) { // Send magic sequence - must be done first, afterwhich error messages can be sent char magic[] = {'G', 'A', 'T', 'O', 'R', '\n'}; - dataSocket->send(magic, sizeof(magic)); + mDataSocket->send(magic, sizeof(magic)); gSessionData->mWaitingOnCommand = true; logg->logMessage("Completed magic sequence"); } - pthread_mutex_init(&sendMutex, NULL); + pthread_mutex_init(&mSendMutex, NULL); } Sender::~Sender() { - delete dataSocket; - dataSocket = NULL; - if (dataFile) { - fclose(dataFile); + delete mDataSocket; + mDataSocket = NULL; + if (mDataFile) { + fclose(mDataFile); } } void Sender::createDataFile(char* apcDir) { - if (apcDir == NULL) + if (apcDir == NULL) { return; + } - dataFileName = (char*)malloc(strlen(apcDir) + 12); - sprintf(dataFileName, "%s/0000000000", apcDir); - dataFile = fopen(dataFileName, "wb"); - if (!dataFile) { - logg->logError(__FILE__, __LINE__, "Failed to open binary file: %s", dataFileName); + mDataFileName = (char*)malloc(strlen(apcDir) + 12); + sprintf(mDataFileName, "%s/0000000000", apcDir); + mDataFile = fopen(mDataFileName, "wb"); + if (!mDataFile) { + logg->logError(__FILE__, __LINE__, "Failed to open binary file: %s", mDataFileName); handleException(); } } @@ -75,32 +76,35 @@ void Sender::writeData(const char* data, int length, int type) { } // Multiple threads call writeData() - pthread_mutex_lock(&sendMutex); + pthread_mutex_lock(&mSendMutex); // Send data over the socket connection - if (dataSocket) { + if (mDataSocket) { // Start alarm alarm(8); // Send data over the socket, sending the type and size first logg->logMessage("Sending data with length %d", length); - dataSocket->send((char*)&type, 1); - dataSocket->send((char*)&length, sizeof(length)); - dataSocket->send((char*)data, length); + if (type != RESPONSE_APC_DATA) { + // type and length already added by the Collector for apc data + mDataSocket->send((char*)&type, 1); + mDataSocket->send((char*)&length, sizeof(length)); + } + mDataSocket->send((char*)data, length); // Stop alarm alarm(0); } // Write data to disk as long as it is not meta data - if (dataFile && type == RESPONSE_APC_DATA) { + if (mDataFile && type == RESPONSE_APC_DATA) { logg->logMessage("Writing data with length %d", length); - // Send data to the data file, storing the size first - if ((fwrite((char*)&length, 1, sizeof(length), dataFile) != sizeof(length)) || (fwrite(data, 1, length, dataFile) != (unsigned int)length)) { - logg->logError(__FILE__, __LINE__, "Failed writing binary file %s", dataFileName); + // Send data to the data file + if (fwrite(data, 1, length, mDataFile) != (unsigned int)length) { + logg->logError(__FILE__, __LINE__, "Failed writing binary file %s", mDataFileName); handleException(); } } - pthread_mutex_unlock(&sendMutex); + pthread_mutex_unlock(&mSendMutex); } diff --git a/daemon/Sender.h b/daemon/Sender.h index 3444b85..98467e0 100644 --- a/daemon/Sender.h +++ b/daemon/Sender.h @@ -29,10 +29,10 @@ public: void writeData(const char* data, int length, int type); void createDataFile(char* apcDir); private: - OlySocket* dataSocket; - FILE* dataFile; - char* dataFileName; - pthread_mutex_t sendMutex; + OlySocket* mDataSocket; + FILE* mDataFile; + char* mDataFileName; + pthread_mutex_t mSendMutex; }; #endif //__SENDER_H__ diff --git a/daemon/SessionData.cpp b/daemon/SessionData.cpp index 4adcfcd..a245369 100644 --- a/daemon/SessionData.cpp +++ b/daemon/SessionData.cpp @@ -27,8 +27,10 @@ void SessionData::initialize() { mLocalCapture = false; mOneShot = false; strcpy(mCoreName, "unknown"); - configurationXMLPath = NULL; - apcDir = NULL; + mConfigurationXMLPath = NULL; + mSessionXMLPath = NULL; + mEventsXMLPath = NULL; + mAPCDir = NULL; mSampleRate = 0; mDuration = 0; mBytes = 0; @@ -46,14 +48,19 @@ void SessionData::initializeCounters() { mPerfCounterTitle[i][0] = 0; mPerfCounterName[i][0] = 0; mPerfCounterDescription[i][0] = 0; + mPerfCounterOperation[i][0] = 0; + mPerfCounterAlias[i][0] = 0; + mPerfCounterDisplay[i][0] = 0; + mPerfCounterUnits[i][0] = 0; mPerfCounterEnabled[i] = 0; mPerfCounterEvent[i] = 0; mPerfCounterColor[i] = 0; mPerfCounterKey[i] = 0; mPerfCounterCount[i] = 0; - mPerfCounterOperation[i][0] = 0; mPerfCounterPerCPU[i] = false; mPerfCounterEBSCapable[i] = false; + mPerfCounterLevel[i] = false; + mPerfCounterAverageSelection[i] = false; } } @@ -75,8 +82,10 @@ void SessionData::parseSessionXML(char* xmlString) { gSessionData->mSampleRate = 10000; } else if (strcmp(session.parameters.sample_rate, "normal") == 0) { gSessionData->mSampleRate = 1000; - } else { // "low" + } else if (strcmp(session.parameters.sample_rate, "low") == 0) { gSessionData->mSampleRate = 100; + } else { + gSessionData->mSampleRate = 0; } gSessionData->mBacktraceDepth = session.parameters.call_stack_unwinding == true ? 128 : 0; gSessionData->mDuration = session.parameters.duration; @@ -97,7 +106,7 @@ void SessionData::parseSessionXML(char* xmlString) { handleException(); } - gSessionData->images = session.parameters.images; - gSessionData->target_path = session.parameters.target_path; - gSessionData->title = session.parameters.title; + gSessionData->mImages = session.parameters.images; + gSessionData->mTargetPath = session.parameters.target_path; + gSessionData->mTitle = session.parameters.title; } diff --git a/daemon/SessionData.h b/daemon/SessionData.h index cc188f8..7daee72 100644 --- a/daemon/SessionData.h +++ b/daemon/SessionData.h @@ -13,11 +13,11 @@ #define MAX_STRING_LEN 80 #define MAX_DESCRIPTION_LEN 400 -#define PROTOCOL_VERSION 8 +#define PROTOCOL_VERSION 9 #define PROTOCOL_DEV 1000 // Differentiates development versions (timestamp) from release versions struct ImageLinkList { - char *path; + char* path; struct ImageLinkList *next; }; @@ -30,11 +30,13 @@ public: void parseSessionXML(char* xmlString); char mCoreName[MAX_STRING_LEN]; - struct ImageLinkList *images; - char* configurationXMLPath; - char* target_path; - char* apcDir; - char* title; + struct ImageLinkList *mImages; + char* mConfigurationXMLPath; + char* mSessionXMLPath; + char* mEventsXMLPath; + char* mTargetPath; + char* mAPCDir; + char* mTitle; bool mWaitingOnCommand; bool mSessionIsActive; @@ -42,7 +44,7 @@ public: bool mOneShot; // halt processing of the driver data until profiling is complete or the buffer is filled int mBacktraceDepth; - int mTotalBufferSize; // approximate number of MB to use for the entire collection buffer, the actual amount is a multiple based on a buffer size retrieved from the driver + int mTotalBufferSize; // number of MB to use for the entire collection buffer int mSampleRate; int mDuration; int mCores; @@ -53,6 +55,10 @@ public: char mPerfCounterTitle[MAX_PERFORMANCE_COUNTERS][MAX_STRING_LEN]; char mPerfCounterName[MAX_PERFORMANCE_COUNTERS][MAX_STRING_LEN]; char mPerfCounterDescription[MAX_PERFORMANCE_COUNTERS][MAX_DESCRIPTION_LEN]; + char mPerfCounterOperation[MAX_PERFORMANCE_COUNTERS][MAX_STRING_LEN]; + char mPerfCounterAlias[MAX_PERFORMANCE_COUNTERS][MAX_STRING_LEN]; + char mPerfCounterDisplay[MAX_PERFORMANCE_COUNTERS][MAX_STRING_LEN]; + char mPerfCounterUnits[MAX_PERFORMANCE_COUNTERS][MAX_STRING_LEN]; int mPerfCounterEnabled[MAX_PERFORMANCE_COUNTERS]; int mPerfCounterEvent[MAX_PERFORMANCE_COUNTERS]; int mPerfCounterColor[MAX_PERFORMANCE_COUNTERS]; @@ -60,7 +66,8 @@ public: int mPerfCounterKey[MAX_PERFORMANCE_COUNTERS]; bool mPerfCounterPerCPU[MAX_PERFORMANCE_COUNTERS]; bool mPerfCounterEBSCapable[MAX_PERFORMANCE_COUNTERS]; - char mPerfCounterOperation[MAX_PERFORMANCE_COUNTERS][MAX_STRING_LEN]; + bool mPerfCounterLevel[MAX_PERFORMANCE_COUNTERS]; + bool mPerfCounterAverageSelection[MAX_PERFORMANCE_COUNTERS]; }; extern SessionData* gSessionData; diff --git a/daemon/SessionXML.cpp b/daemon/SessionXML.cpp index 4604d7c..f1a8258 100644 --- a/daemon/SessionXML.cpp +++ b/daemon/SessionXML.cpp @@ -11,6 +11,7 @@ #include #include "SessionXML.h" #include "Logging.h" +#include "OlyUtility.h" extern void handleException(); @@ -28,7 +29,7 @@ static const char* ATTR_OUTPUT_PATH = "output_path"; static const char* ATTR_DURATION = "duration"; static const char* ATTR_PATH = "path"; -SessionXML::SessionXML(const char * str) { +SessionXML::SessionXML(const char* str) { parameters.title = 0; parameters.uuid[0] = 0; parameters.target_path = 0; @@ -50,70 +51,92 @@ SessionXML::~SessionXML() { } void SessionXML::parse() { - XMLReader reader(mSessionXML); - char * tag = reader.nextTag(); - while(tag != 0) { - if (strcmp(tag, TAG_SESSION) == 0) { - sessionTag(&reader); - return; - } - tag = reader.nextTag(); + mxml_node_t *tree; + mxml_node_t *node; + + tree = mxmlLoadString(NULL, mSessionXML, MXML_NO_CALLBACK); + node = mxmlFindElement(tree, tree, TAG_SESSION, NULL, NULL, MXML_DESCEND); + + if (node) { + sessionTag(tree, node); + mxmlDelete(tree); + return; } logg->logError(__FILE__, __LINE__, "No session tag found in the session.xml file"); handleException(); } -void SessionXML::sessionTag(XMLReader* in) { - char* tempBuffer = (char*)malloc(PATH_MAX); - int version = in->getAttributeAsInteger(ATTR_VERSION, 0); +void SessionXML::sessionTag(mxml_node_t *tree, mxml_node_t *node) { + int version = 0; + if (mxmlElementGetAttr(node, ATTR_VERSION)) version = strtol(mxmlElementGetAttr(node, ATTR_VERSION), NULL, 10); if (version != 1) { logg->logError(__FILE__, __LINE__, "Invalid session.xml version: %d", version); handleException(); } - in->getAttribute(ATTR_TITLE, tempBuffer, PATH_MAX, "unnamed"); - parameters.title = strdup(tempBuffer); // freed when the child process exits - if (parameters.title == NULL) { - logg->logError(__FILE__, __LINE__, "failed to allocate parameters.title (%d bytes)", strlen(tempBuffer)); - handleException(); + // allocate strings + if (mxmlElementGetAttr(node, ATTR_TITLE)) { + parameters.title = strdup(mxmlElementGetAttr(node, ATTR_TITLE)); // freed when the child process exits + if (parameters.title == NULL) { + logg->logError(__FILE__, __LINE__, "failed to allocate parameters.title"); + handleException(); + } } - in->getAttribute(ATTR_UUID, parameters.uuid, sizeof(parameters.uuid), ""); - parameters.duration = in->getAttributeAsInteger(ATTR_DURATION, 0); - parameters.call_stack_unwinding = in->getAttributeAsBoolean(ATTR_CALL_STACK_UNWINDING, true); - in->getAttribute(ATTR_BUFFER_MODE, parameters.buffer_mode, sizeof(parameters.buffer_mode), "normal"); - in->getAttribute(ATTR_SAMPLE_RATE, parameters.sample_rate, sizeof(parameters.sample_rate), ""); - in->getAttribute(ATTR_TARGET_PATH, tempBuffer, PATH_MAX, ""); - parameters.target_path = strdup(tempBuffer); // freed when the child process exits - if (parameters.target_path == NULL) { - logg->logError(__FILE__, __LINE__, "failed to allocate parameters.target_path (%d bytes)", strlen(tempBuffer)); - handleException(); + if (mxmlElementGetAttr(node, ATTR_TARGET_PATH)) { + parameters.target_path = strdup(mxmlElementGetAttr(node, ATTR_TARGET_PATH)); // freed when the child process exits + if (parameters.target_path == NULL) { + logg->logError(__FILE__, __LINE__, "failed to allocate parameters.target_path"); + handleException(); + } } - in->getAttribute(ATTR_OUTPUT_PATH, tempBuffer, PATH_MAX, ""); - parameters.output_path = strdup(tempBuffer); // freed when the child process exits - if (parameters.output_path == NULL) { - logg->logError(__FILE__, __LINE__, "failed to allocate parameters.output_path (%d bytes)", strlen(tempBuffer)); - handleException(); + if (mxmlElementGetAttr(node, ATTR_OUTPUT_PATH)) { + parameters.output_path = strdup(mxmlElementGetAttr(node, ATTR_OUTPUT_PATH)); // freed when the child process exits + if (parameters.output_path == NULL) { + logg->logError(__FILE__, __LINE__, "failed to allocate parameters.output_path"); + handleException(); + } } - free(tempBuffer); + // copy to pre-allocated strings + if (mxmlElementGetAttr(node, ATTR_UUID)) { + strncpy(parameters.uuid, mxmlElementGetAttr(node, ATTR_UUID), sizeof(parameters.uuid)); + parameters.uuid[sizeof(parameters.uuid) - 1] = 0; // strncpy does not guarantee a null-terminated string + } + if (mxmlElementGetAttr(node, ATTR_BUFFER_MODE)) { + strncpy(parameters.buffer_mode, mxmlElementGetAttr(node, ATTR_BUFFER_MODE), sizeof(parameters.buffer_mode)); + parameters.buffer_mode[sizeof(parameters.buffer_mode) - 1] = 0; // strncpy does not guarantee a null-terminated string + } + if (mxmlElementGetAttr(node, ATTR_SAMPLE_RATE)) { + strncpy(parameters.sample_rate, mxmlElementGetAttr(node, ATTR_SAMPLE_RATE), sizeof(parameters.sample_rate)); + parameters.sample_rate[sizeof(parameters.sample_rate) - 1] = 0; // strncpy does not guarantee a null-terminated string + } + + // integers/bools + parameters.call_stack_unwinding = util->stringToBool(mxmlElementGetAttr(node, ATTR_CALL_STACK_UNWINDING), false); + if (mxmlElementGetAttr(node, ATTR_DURATION)) parameters.duration = strtol(mxmlElementGetAttr(node, ATTR_DURATION), NULL, 10); - char * tag = in->nextTag(); - while(tag != 0) { - if (strcmp(tag, TAG_IMAGE) == 0) { - sessionImage(in); + // parse subtags + node = mxmlGetFirstChild(node); + while (node) { + if (mxmlGetType(node) != MXML_ELEMENT) { + node = mxmlWalkNext(node, tree, MXML_NO_DESCEND); + continue; + } + if (strcmp(TAG_IMAGE, mxmlGetElement(node)) == 0) { + sessionImage(node); } - tag = in->nextTag(); + node = mxmlWalkNext(node, tree, MXML_NO_DESCEND); } } -void SessionXML::sessionImage(XMLReader* in) { - int length = in->getAttributeLength(ATTR_PATH); +void SessionXML::sessionImage(mxml_node_t *node) { + int length = strlen(mxmlElementGetAttr(node, ATTR_PATH)); struct ImageLinkList *image; image = (struct ImageLinkList *)malloc(sizeof(struct ImageLinkList)); - image->path = (char *)malloc(length + 1); - in->getAttribute(ATTR_PATH, image->path, length + 1, ""); + image->path = (char*)malloc(length + 1); + image->path = strdup(mxmlElementGetAttr(node, ATTR_PATH)); image->next = parameters.images; parameters.images = image; } diff --git a/daemon/SessionXML.h b/daemon/SessionXML.h index 4649685..c2b5489 100644 --- a/daemon/SessionXML.h +++ b/daemon/SessionXML.h @@ -9,7 +9,7 @@ #ifndef SESSION_XML_H #define SESSION_XML_H -#include "XMLReader.h" +#include "mxml/mxml.h" #include "SessionData.h" struct ConfigParameters { @@ -26,15 +26,15 @@ struct ConfigParameters { class SessionXML { public: - SessionXML(const char * str); + SessionXML(const char* str); ~SessionXML(); void parse(); ConfigParameters parameters; private: char* mSessionXML; char* mPath; - void sessionTag(XMLReader* in); - void sessionImage(XMLReader* in); + void sessionTag(mxml_node_t *tree, mxml_node_t *node); + void sessionImage(mxml_node_t *node); }; #endif // SESSION_XML_H diff --git a/daemon/StreamlineSetup.cpp b/daemon/StreamlineSetup.cpp index 5662ee8..53729ab 100644 --- a/daemon/StreamlineSetup.cpp +++ b/daemon/StreamlineSetup.cpp @@ -14,11 +14,8 @@ #include #include #include -#include "XMLOut.h" #include "Sender.h" #include "Logging.h" -#include "XMLReader.h" -#include "RequestXML.h" #include "OlyUtility.h" #include "SessionData.h" #include "CapturedXML.h" @@ -27,15 +24,24 @@ extern void handleException(); -static const char* TAG_SESSION = "session"; -static const char* TAG_CONFIGURATIONS = "configurations"; +static const char* TAG_SESSION = "session"; +static const char* TAG_REQUEST = "request"; +static const char* TAG_CONFIGURATIONS = "configurations"; + +static const char* ATTR_PROTOCOL = "protocol"; +static const char* ATTR_EVENTS = "events"; +static const char* ATTR_CONFIGURATION = "configuration"; +static const char* ATTR_COUNTERS = "counters"; +static const char* ATTR_SESSION = "session"; +static const char* ATTR_CAPTURED = "captured"; +static const char* ATTR_DEFAULTS = "defaults"; StreamlineSetup::StreamlineSetup(OlySocket* s) { bool ready = false; - char *data = NULL; + char* data = NULL; int type; - socket = s; + mSocket = s; mSessionXML = NULL; // Receive commands from Streamline (master) @@ -73,13 +79,14 @@ StreamlineSetup::StreamlineSetup(OlySocket* s) { handleException(); } - delete(data); + free(data); } } StreamlineSetup::~StreamlineSetup() { - if (mSessionXML) + if (mSessionXML) { free(mSessionXML); + } } char* StreamlineSetup::readCommand(int* command) { @@ -88,7 +95,7 @@ char* StreamlineSetup::readCommand(int* command) { int response, length; // receive type - response = socket->receiveNBytes(&type, sizeof(type)); + response = mSocket->receiveNBytes(&type, sizeof(type)); // After receiving a single byte, we are no longer waiting on a command gSessionData->mWaitingOnCommand = false; @@ -99,7 +106,7 @@ char* StreamlineSetup::readCommand(int* command) { } // receive length - response = socket->receiveNBytes((char*)&length, sizeof(length)); + response = mSocket->receiveNBytes((char*)&length, sizeof(length)); if (response < 0) { logg->logError(__FILE__, __LINE__, "Target error: Unexpected socket disconnect"); handleException(); @@ -119,7 +126,7 @@ char* StreamlineSetup::readCommand(int* command) { } // receive data - response = socket->receiveNBytes(data, length); + response = mSocket->receiveNBytes(data, length); if (response < 0) { logg->logError(__FILE__, __LINE__, "Target error: Unexpected socket disconnect"); handleException(); @@ -135,29 +142,31 @@ char* StreamlineSetup::readCommand(int* command) { } void StreamlineSetup::handleRequest(char* xml) { - RequestXML request(xml); + mxml_node_t *tree, *node; - if (request.parameters.protocol) { + tree = mxmlLoadString(NULL, xml, MXML_NO_CALLBACK); + if ((node = mxmlFindElement(tree, tree, TAG_REQUEST, ATTR_PROTOCOL, NULL, MXML_DESCEND_FIRST)) && util->stringToBool(mxmlElementGetAttr(node, ATTR_PROTOCOL), false)) { sendProtocol(); logg->logMessage("Sent protocol xml response"); - } else if (request.parameters.events) { + } else if ((node = mxmlFindElement(tree, tree, TAG_REQUEST, ATTR_EVENTS, NULL, MXML_DESCEND_FIRST)) && util->stringToBool(mxmlElementGetAttr(node, ATTR_EVENTS), false)) { sendEvents(); logg->logMessage("Sent events xml response"); - } else if (request.parameters.configuration) { + } else if ((node = mxmlFindElement(tree, tree, TAG_REQUEST, ATTR_CONFIGURATION, NULL, MXML_DESCEND_FIRST)) && util->stringToBool(mxmlElementGetAttr(node, ATTR_CONFIGURATION), false)) { sendConfiguration(); logg->logMessage("Sent configuration xml response"); - } else if (request.parameters.counters) { + } else if ((node = mxmlFindElement(tree, tree, TAG_REQUEST, ATTR_COUNTERS, NULL, MXML_DESCEND_FIRST)) && util->stringToBool(mxmlElementGetAttr(node, ATTR_COUNTERS), false)) { sendCounters(); logg->logMessage("Sent counters xml response"); - } else if (request.parameters.session) { + } else if ((node = mxmlFindElement(tree, tree, TAG_REQUEST, ATTR_SESSION, NULL, MXML_DESCEND_FIRST)) && util->stringToBool(mxmlElementGetAttr(node, ATTR_SESSION), false)) { sendData(mSessionXML, strlen(mSessionXML), RESPONSE_XML); logg->logMessage("Sent session xml response"); - } else if (request.parameters.captured) { + } else if ((node = mxmlFindElement(tree, tree, TAG_REQUEST, ATTR_CAPTURED, NULL, MXML_DESCEND_FIRST)) && util->stringToBool(mxmlElementGetAttr(node, ATTR_CAPTURED), false)) { CapturedXML capturedXML; - const char* capturedText = capturedXML.getXML(); + char* capturedText = capturedXML.getXML(); sendData(capturedText, strlen(capturedText), RESPONSE_XML); + free(capturedText); logg->logMessage("Sent captured xml response"); - } else if (request.parameters.defaults) { + } else if ((node = mxmlFindElement(tree, tree, TAG_REQUEST, ATTR_DEFAULTS, NULL, MXML_DESCEND_FIRST)) && util->stringToBool(mxmlElementGetAttr(node, ATTR_DEFAULTS), false)) { sendDefaults(); logg->logMessage("Sent default configuration xml response"); } else { @@ -165,67 +174,60 @@ void StreamlineSetup::handleRequest(char* xml) { sendData(error, strlen(error), RESPONSE_NAK); logg->logMessage("Received unknown request:\n%s", xml); } + + mxmlDelete(tree); } -typedef enum {UNKNOWN, SESSION_XML, CONFIGURATION_XML} delivery_type_t; void StreamlineSetup::handleDeliver(char* xml) { - delivery_type_t type = UNKNOWN; + mxml_node_t *tree; // Determine xml type - XMLReader reader(xml); - char * tag = reader.nextTag(); - while(tag != 0) { - if (strcmp(tag, TAG_SESSION) == 0) { - type = SESSION_XML; - break; - } else if (strcmp(tag, TAG_CONFIGURATIONS) == 0) { - type = CONFIGURATION_XML; - break; + tree = mxmlLoadString(NULL, xml, MXML_NO_CALLBACK); + if (mxmlFindElement(tree, tree, TAG_SESSION, NULL, NULL, MXML_DESCEND_FIRST)) { + // Session XML + gSessionData->parseSessionXML(xml); + + // Save xml + mSessionXML = strdup(xml); + if (mSessionXML == NULL) { + logg->logError(__FILE__, __LINE__, "malloc failed for size %d", strlen(xml) + 1); + handleException(); } - tag = reader.nextTag(); + sendData(NULL, 0, RESPONSE_ACK); + logg->logMessage("Received session xml"); + } else if (mxmlFindElement(tree, tree, TAG_CONFIGURATIONS, NULL, NULL, MXML_DESCEND_FIRST)) { + // Configuration XML + writeConfiguration(xml); + sendData(NULL, 0, RESPONSE_ACK); + logg->logMessage("Received configuration xml"); + } else { + // Unknown XML + logg->logMessage("Received unknown XML delivery type"); + sendData(NULL, 0, RESPONSE_NAK); } - switch (type) { - case UNKNOWN: - logg->logMessage("Received unknown delivery type: %d", type); - sendData(NULL, 0, RESPONSE_NAK); - break; - case SESSION_XML: - // Parse the session xml - gSessionData->parseSessionXML(xml); - - // Save xml - mSessionXML = strdup(xml); - if (mSessionXML == NULL) { - logg->logError(__FILE__, __LINE__, "malloc failed for size %d", strlen(xml) + 1); - handleException(); - } - sendData(NULL, 0, RESPONSE_ACK); - logg->logMessage("Received session xml"); - break; - case CONFIGURATION_XML: - writeConfiguration(xml); - sendData(NULL, 0, RESPONSE_ACK); - logg->logMessage("Received configuration xml"); - break; - } + mxmlDelete(tree); } void StreamlineSetup::sendData(const char* data, int length, int type) { - socket->send((char*)&type, 1); - socket->send((char*)&length, sizeof(length)); - socket->send((char*)data, length); + mSocket->send((char*)&type, 1); + mSocket->send((char*)&length, sizeof(length)); + mSocket->send((char*)data, length); } void StreamlineSetup::sendProtocol() { - XMLOut out; - out.xmlHeader(); + mxml_node_t *xml; + mxml_node_t *protocol; - out.startElement("protocol"); - out.attributeInt("version", PROTOCOL_VERSION); - out.endElement("protocol"); + xml = mxmlNewXML("1.0"); + protocol = mxmlNewElement(xml, "protocol"); + mxmlElementSetAttrf(protocol, "version", "%d", PROTOCOL_VERSION); - sendString(out.getXmlString(), RESPONSE_XML); + char* string = mxmlSaveAllocString(xml, mxmlWhitespaceCB); + sendString(string, RESPONSE_XML); + + free(string); + mxmlDelete(xml); } void StreamlineSetup::sendEvents() { @@ -234,8 +236,12 @@ void StreamlineSetup::sendEvents() { char* buffer; unsigned int size = 0; - util->getApplicationFullPath(path, PATH_MAX); - strncat(path, "events.xml", PATH_MAX - strlen(path) - 1); + if (gSessionData->mEventsXMLPath) { + strncpy(path, gSessionData->mEventsXMLPath, PATH_MAX); + } else { + util->getApplicationFullPath(path, PATH_MAX); + strncat(path, "events.xml", PATH_MAX - strlen(path) - 1); + } buffer = util->readFromDisk(path, &size); if (buffer == NULL) { logg->logMessage("Unable to locate events.xml, using default"); @@ -274,8 +280,10 @@ void StreamlineSetup::sendDefaults() { #include void StreamlineSetup::sendCounters() { - XMLOut out; struct dirent *ent; + mxml_node_t *xml; + mxml_node_t *counters; + mxml_node_t *counter; // counters.xml is simply a file listing of /dev/gator/events DIR* dir = opendir("/dev/gator/events"); @@ -284,27 +292,29 @@ void StreamlineSetup::sendCounters() { handleException(); } - out.xmlHeader(); - out.startElement("counters"); + xml = mxmlNewXML("1.0"); + counters = mxmlNewElement(xml, "counters"); while ((ent = readdir(dir)) != NULL) { // skip hidden files, current dir, and parent dir if (ent->d_name[0] == '.') continue; - out.startElement("counter"); - out.attributeString("name", ent->d_name); - out.endElement("counter"); + counter = mxmlNewElement(counters, "counter"); + mxmlElementSetAttr(counter, "name", ent->d_name); } - out.endElement("counters"); closedir (dir); - sendString(out.getXmlString(), RESPONSE_XML); + char* string = mxmlSaveAllocString(xml, mxmlWhitespaceCB); + sendString(string, RESPONSE_XML); + + free(string); + mxmlDelete(xml); } void StreamlineSetup::writeConfiguration(char* xml) { char* path = (char*)malloc(PATH_MAX); - if (gSessionData->configurationXMLPath) { - strncpy(path, gSessionData->configurationXMLPath, PATH_MAX); + if (gSessionData->mConfigurationXMLPath) { + strncpy(path, gSessionData->mConfigurationXMLPath, PATH_MAX); } else { util->getApplicationFullPath(path, PATH_MAX); strncat(path, "configuration.xml", PATH_MAX - strlen(path) - 1); diff --git a/daemon/StreamlineSetup.h b/daemon/StreamlineSetup.h index 10327e0..c46ae08 100644 --- a/daemon/StreamlineSetup.h +++ b/daemon/StreamlineSetup.h @@ -26,8 +26,8 @@ public: StreamlineSetup(OlySocket *socket); ~StreamlineSetup(); private: - int numConnections; - OlySocket* socket; + int mNumConnections; + OlySocket* mSocket; char* mSessionXML; char* readCommand(int*); diff --git a/daemon/XMLOut.cpp b/daemon/XMLOut.cpp deleted file mode 100644 index c194fc0..0000000 --- a/daemon/XMLOut.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/** - * Copyright (C) ARM Limited 2010-2012. 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 -#include -#include -#include "XMLOut.h" - -XMLOut::XMLOut() { - indent = 0; - incomplete = false; - xml_string[0] = 0; -} - -XMLOut::~XMLOut() { -} - -void XMLOut::writeTabs() { - for (int i = 0; i < indent; i++) { - writeData(" "); - } -} - -void XMLOut::encodeAttributeData(const char* data) { - if (data) { - while (*data) { - char ch = *data++; - - if (ch == '<') { - writeData("<"); - } else if (ch == '>') { - writeData(">"); - } else if (ch == '&') { - writeData("&"); - } else if (ch == '"') { - writeData("""); - } else if (ch == '\'') { - writeData("'"); - } else if (ch >= ' ' && ch <= '~') { - writeData("%c",ch); - } else { - writeData("&#%u;",(unsigned int)ch); - } - } - } -} - -void XMLOut::writeData(const char *format, ...) { - va_list ap; - - va_start(ap, format); - vsnprintf(temp_buffer, sizeof(temp_buffer), format, ap); - va_end(ap); - - strncat(xml_string, temp_buffer, sizeof(xml_string) - strlen(xml_string) - 1); -} - -const XMLOut & XMLOut::xmlHeader(void) { - writeData("\n"); - incomplete = false; - return *this; -} - -const XMLOut & XMLOut::comment(const char* text, const bool newline) { - if (incomplete) { - writeData(">\n"); - } - writeTabs(); - writeData("", text); - if (newline) { - writeData("\n"); - } - incomplete = false; - return *this; -} - -const XMLOut & XMLOut::startElement(const char* tag) { - if (incomplete) { - writeData(">\n"); - } - writeTabs(); - writeData("<%s", tag); - incomplete = true; - indent++; - return *this; -} - -const XMLOut & XMLOut::startElement(const char* tag, int index) { - if (incomplete) { - writeData(">\n"); - } - writeTabs(); - writeData("", index); - writeData("<%s", tag); - incomplete = true; - indent++; - return *this; -} - -const XMLOut & XMLOut::endElement(const char* tag) { - indent--; - if (indent < 0) { - indent = 0; - } - if (incomplete) { - writeData("/>\n"); - incomplete = false; - } else { - writeTabs(); - writeData("\n", tag); - } - return *this; -} - -const XMLOut & XMLOut::attributeString(const char* name, const char* value) { - writeData(" %s=\"", name); - encodeAttributeData(value); - writeData("\""); - return *this; -} - -const XMLOut & XMLOut::attributeInt(const char* name, int value) { - writeData(" %s=\"%d\"", name, value); - return *this; -} - -const XMLOut & XMLOut::attributeUInt(const char* name, unsigned int value) { - writeData(" %s=\"%u\"", name, value); - return *this; -} - -const XMLOut & XMLOut::attributeLong(const char* name, long value) { - writeData(" %s=\"%ld\"", name, value); - return *this; -} - -const XMLOut & XMLOut::attributeULong(const char* name, unsigned long value) { - writeData(" %s=\"%lu\"", name, value); - return *this; -} - -const XMLOut & XMLOut::attributeLongLong(const char* name, long long value) { - writeData(" %s=\"%lld\"", name, value); - return *this; -} - -const XMLOut & XMLOut::attributeULongLong(const char* name, unsigned long long value) { - writeData(" %s=\"%llu\"", name, value); - return *this; -} - -const XMLOut & XMLOut::attributeDouble(const char* name, double value) { - writeData(" %s=\"%f\"", name, value); - return *this; -} - -const XMLOut & XMLOut::attributeBool(const char* name, bool value) { - writeData(" %s=\"%s\"", name, value ? "yes" : "no"); - return *this; -} - -const XMLOut & XMLOut::attributeHex4(const char* name, int value) { - writeData(" %s=\"0x%04x\"", name, value); - return *this; -} - -const XMLOut & XMLOut::attributeHex8(const char* name, int value) { - writeData(" %s=\"0x%08x\"", name, value); - return *this; -} diff --git a/daemon/XMLOut.h b/daemon/XMLOut.h deleted file mode 100644 index 3af253d..0000000 --- a/daemon/XMLOut.h +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright (C) ARM Limited 2010-2012. 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. - */ - -#ifndef __XMLOUT_H -#define __XMLOUT_H - -class XMLOut { - int indent; - bool incomplete; - char temp_buffer[4096]; // arbitrarilly large buffer to hold variable arguments - char xml_string[64*1024]; // arbitrarilly large buffer to hold an xml file output by the daemon - - void writeTabs(); - void encodeAttributeData(const char* data); - void writeData(const char *format, ...); - -public: - XMLOut(); - ~XMLOut(); - char* getXmlString() {return xml_string;} - void clearXmlString() {xml_string[0]=0;} - const XMLOut & xmlHeader(void); - const XMLOut & comment(const char* text, const bool newline); - const XMLOut & startElement(const char* tag); - const XMLOut & startElement(const char* tag, int index); - const XMLOut & endElement(const char* tag); - const XMLOut & attributeString(const char* name, const char* value); - const XMLOut & attributeInt(const char* name, int value); - const XMLOut & attributeUInt(const char* name, unsigned int value); - const XMLOut & attributeLong(const char* name, long value); - const XMLOut & attributeULong(const char* name, unsigned long value); - const XMLOut & attributeLongLong(const char* name, long long value); - const XMLOut & attributeULongLong(const char* name, unsigned long long value); - const XMLOut & attributeDouble(const char* name, double value); - const XMLOut & attributeBool(const char* name, bool value); - const XMLOut & attributeHex4(const char* name, int value); - const XMLOut & attributeHex8(const char* name, int value); -}; - -#endif // __XMLOUT_H diff --git a/daemon/XMLReader.cpp b/daemon/XMLReader.cpp deleted file mode 100644 index 77c76e2..0000000 --- a/daemon/XMLReader.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/** - * Copyright (C) ARM Limited 2010-2012. 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 -#include -#include -#include "XMLReader.h" -extern void handleException(); - -XMLReader::XMLReader(const char* xmlstring) { - mPtr = (char*)xmlstring; - mNoMore = false; - mFirstTime = true; -} - -XMLReader::~XMLReader() { -} - -char* XMLReader::nextTag() { - static char tag[128]; // arbitrarily set max tag size to 127 characters + nul - - // Check if past the end of the root tag - if (mNoMore) return NULL; - - // Find start character - mPtr = strchr(mPtr, '<'); - - if (mPtr == NULL) return mPtr; - - // Skip tag if it begins with '); - if (end == NULL) - return 0; - - // Check if tag has no attributes - char* tagend = strchr(mPtr, '>'); - if (tagend < end) end = tagend; - - // Check the tag name length - unsigned int length = (int)end - (int)mPtr; - if (length > sizeof(tag) - 1) { - // tag name too long, skip it - return nextTag(); - } - - // Return the tag name - strncpy(tag, mPtr, length); - tag[length] = 0; - - // Mark the root tag - if (mFirstTime) { - mEndXML[0] = '/'; - strcpy(&mEndXML[1], tag); - mFirstTime = false; - } else if (strcmp(tag, mEndXML) == 0) { - // End of root tag found - mNoMore = true; - } - - return tag; -} - -void XMLReader::getAttribute(const char* name, char* value, int maxSize, const char* defValue) { - char searchString[128]; - - // Set up default - strncpy(value, defValue, maxSize - 1); - value[maxSize - 1] = 0; - - // Determine search string by ending the name with =" - if (strlen(name) > sizeof(searchString) - 3) return; - strcpy(searchString, name); - strcat(searchString, "=\""); - - // Find the beginning of the attribute's search string - char* begin = strstr(mPtr, searchString); - if (begin == NULL) return; - - // Find the beginning of the attribute's value - begin += strlen(searchString); - - // Find the end of the current tag to make sure the attribute exists within the tag - char* endtag = strchr(mPtr, '>'); - if (endtag < begin) return; - - // Find the end of the attribute's value - char* end = strchr(begin, '"'); - if (end == NULL) return; - - // Determine length - int length = (int)end - (int)begin; - if (length > maxSize - 1) return; - - strncpy(value, begin, length); - value[length] = 0; -} - -int XMLReader::getAttributeAsInteger(const char* name, int defValue) { - char value[32]; - getAttribute(name, value, sizeof(value), ""); - if (value[0] == 0) return defValue; - if (value[0] == '0' && value[1] == 'x') { - return (int) strtoul(&value[2], (char**)NULL, 16); - } - return strtol(value, NULL, 10); -} - -bool XMLReader::getAttributeAsBoolean(const char* name, bool defValue) { - char value[32]; - getAttribute(name, value, sizeof(value), ""); - if (value[0] == 0) return defValue; - - // Convert to lowercase - int i = 0; - while (value[i]) { - value[i] = tolower(value[i]); - i++; - } - - if (strcmp(value, "true") == 0 || strcmp(value, "yes") == 0 || strcmp(value, "1") == 0 || strcmp(value, "on") == 0) return true; - else if (strcmp(value, "false") == 0 || strcmp(value, "no") == 0 || strcmp(value, "0") == 0 || strcmp(value, "off") == 0) return false; - else return defValue; -} - -int XMLReader::getAttributeLength(const char* name) { - char searchString[128]; // arbitrarily large amount - - // Determine search string by ending the name with =" - if (strlen(name) > sizeof(searchString) - 3) return 0; - strcpy(searchString, name); - strcat(searchString, "=\""); - - // Find the beginning of the attribute's search string - char* begin = strstr(mPtr, searchString); - if (begin == NULL) return 0; - - // Find the beginning of the attribute's value - begin += strlen(searchString); - - // Find the end of the current tag to make sure the attribute exists within the tag - char* endtag = strchr(mPtr, '>'); - if (endtag < begin) return 0; - - // Find the end of the attribute's value - char* end = strchr(begin, '"'); - if (end == NULL) return 0; - - // Determine length - return (int)end - (int)begin; -} diff --git a/daemon/XMLReader.h b/daemon/XMLReader.h deleted file mode 100644 index ac0098a..0000000 --- a/daemon/XMLReader.h +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (C) ARM Limited 2010-2012. 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. - */ - -#ifndef _XMLREADER_H -#define _XMLREADER_H - -class XMLReader { -public: - XMLReader(const char* xmlstring); - ~XMLReader(); - char* nextTag(); - void getAttribute(const char* name, char* value, int maxSize, const char* defValue); - int getAttributeAsInteger(const char* name, int defValue); - bool getAttributeAsBoolean(const char* name, bool defValue); - int getAttributeLength(const char* name); -private: - char* mPtr; - bool mFirstTime, mNoMore; - char mEndXML[128]; -}; - -#endif // _XMLREADER_H diff --git a/daemon/configuration.xml b/daemon/configuration.xml index 0f91065..9d083d5 100644 --- a/daemon/configuration.xml +++ b/daemon/configuration.xml @@ -1,39 +1,51 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/daemon/events-ARM11.xml b/daemon/events-ARM11.xml index 5742271..c9d188f 100644 --- a/daemon/events-ARM11.xml +++ b/daemon/events-ARM11.xml @@ -4,18 +4,18 @@ - + - - + + - + diff --git a/daemon/events-ARM11MPCore.xml b/daemon/events-ARM11MPCore.xml index 6da9ecb..74a29e6 100644 --- a/daemon/events-ARM11MPCore.xml +++ b/daemon/events-ARM11MPCore.xml @@ -4,7 +4,7 @@ - + @@ -12,11 +12,11 @@ - - + + - + @@ -27,4 +27,4 @@ - \ No newline at end of file + diff --git a/daemon/events-Cortex-A15.xml b/daemon/events-Cortex-A15.xml index 86a3d17..74c7290 100644 --- a/daemon/events-Cortex-A15.xml +++ b/daemon/events-Cortex-A15.xml @@ -7,31 +7,31 @@ - + - + - - - + + + - - - - - - - - + + + + + + + + @@ -52,26 +52,25 @@ - - + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/daemon/events-Cortex-A5.xml b/daemon/events-Cortex-A5.xml index 3683947..0a70a62 100644 --- a/daemon/events-Cortex-A5.xml +++ b/daemon/events-Cortex-A5.xml @@ -3,25 +3,25 @@ - + - - - + + + - - - + + + diff --git a/daemon/events-Cortex-A7.xml b/daemon/events-Cortex-A7.xml old mode 100644 new mode 100755 index db9f180..1a55817 --- a/daemon/events-Cortex-A7.xml +++ b/daemon/events-Cortex-A7.xml @@ -5,37 +5,37 @@ - + - - - + + + - - - - + + + + - - - - - + + + + + - - + + @@ -47,4 +47,3 @@ - \ No newline at end of file diff --git a/daemon/events-Cortex-A8.xml b/daemon/events-Cortex-A8.xml index f3396b7..02260ba 100644 --- a/daemon/events-Cortex-A8.xml +++ b/daemon/events-Cortex-A8.xml @@ -5,30 +5,30 @@ - + - - - + + + - - - + + + - - + + @@ -55,4 +55,3 @@ - \ No newline at end of file diff --git a/daemon/events-Cortex-A9.xml b/daemon/events-Cortex-A9.xml index 1edb54a..363862c 100644 --- a/daemon/events-Cortex-A9.xml +++ b/daemon/events-Cortex-A9.xml @@ -7,28 +7,28 @@ - + - - + + - - + + - - - + + + - - + + @@ -37,13 +37,13 @@ - + - - - - - + + + + + @@ -53,9 +53,9 @@ - - - + + + @@ -64,4 +64,3 @@ - \ No newline at end of file diff --git a/daemon/events-Krait-architected.xml b/daemon/events-Krait-architected.xml old mode 100644 new mode 100755 index 46d6a61..cc3f4e5 --- a/daemon/events-Krait-architected.xml +++ b/daemon/events-Krait-architected.xml @@ -5,24 +5,23 @@ - + - - - + + + - - - + + + - \ No newline at end of file diff --git a/daemon/events-L2C-310.xml b/daemon/events-L2C-310.xml index b1cd8df..35592cf 100644 --- a/daemon/events-L2C-310.xml +++ b/daemon/events-L2C-310.xml @@ -3,19 +3,19 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/daemon/events-Linux.xml b/daemon/events-Linux.xml index 338e160..85ce63c 100644 --- a/daemon/events-Linux.xml +++ b/daemon/events-Linux.xml @@ -1,16 +1,17 @@ - - + + - - - - - - - + + + + - + + + + + diff --git a/daemon/events-Mali-400.xml b/daemon/events-Mali-400.xml index f64fec8..895d0c8 100644 --- a/daemon/events-Mali-400.xml +++ b/daemon/events-Mali-400.xml @@ -353,7 +353,9 @@ - + + + diff --git a/daemon/events-Scorpion.xml b/daemon/events-Scorpion.xml index 8ad196a..6c2b6f8 100644 --- a/daemon/events-Scorpion.xml +++ b/daemon/events-Scorpion.xml @@ -5,109 +5,108 @@ - + - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/daemon/events-ScorpionMP.xml b/daemon/events-ScorpionMP.xml index bd13b8a..87f5657 100644 --- a/daemon/events-ScorpionMP.xml +++ b/daemon/events-ScorpionMP.xml @@ -5,92 +5,91 @@ - + - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/daemon/main.cpp b/daemon/main.cpp index 51237cb..df6913b 100644 --- a/daemon/main.cpp +++ b/daemon/main.cpp @@ -36,7 +36,7 @@ static bool driverMountedAtStart = false; struct cmdline_t { int port; - char* sessionXML; + char* module; }; void cleanUp() { @@ -101,18 +101,20 @@ void child_exit(int signum) { // retval: -1 = failure; 0 = was already mounted; 1 = successfully mounted int mountGatorFS() { // If already mounted, - if (access("/dev/gator/buffer", F_OK) == 0) + if (access("/dev/gator/buffer", F_OK) == 0) { return 0; + } // else, mount the filesystem mkdir("/dev/gator", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); - if (mount("nodev", "/dev/gator", "gatorfs", 0, NULL) != 0) + if (mount("nodev", "/dev/gator", "gatorfs", 0, NULL) != 0) { return -1; - else + } else { return 1; + } } -int setupFilesystem() { +int setupFilesystem(char* module) { int retval; // Verify root permissions @@ -122,6 +124,17 @@ int setupFilesystem() { handleException(); } + if (module) { + // unmount and rmmod if the module was specified on the commandline, i.e. ensure that the specified module is indeed running + shutdownFilesystem(); + + // if still mounted + if (access("/dev/gator/buffer", F_OK) == 0) { + logg->logError(__FILE__, __LINE__, "Unable to remove the running gator.ko. Manually remove the module or use the running module by not specifying one on the commandline"); + handleException(); + } + } + retval = mountGatorFS(); if (retval == 1) { logg->logMessage("Driver already running at startup"); @@ -131,22 +144,25 @@ int setupFilesystem() { driverRunningAtStart = driverMountedAtStart = true; } else { char command[256]; // arbitrarily large amount - - // Is the driver co-located in the same directory? - if (util->getApplicationFullPath(command, sizeof(command)) != 0) { // allow some buffer space - logg->logMessage("Unable to determine the full path of gatord, the cwd will be used"); + char location[256]; // arbitrarily large amount + + if (module) { + strncpy(location, module, sizeof(location)); + } else { + // Is the driver co-located in the same directory? + if (util->getApplicationFullPath(location, sizeof(location)) != 0) { // allow some buffer space + logg->logMessage("Unable to determine the full path of gatord, the cwd will be used"); + } + strncat(location, "gator.ko", sizeof(location) - strlen(location) - 1); } - strcat(command, "gator.ko"); - if (access(command, F_OK) == -1) { - logg->logError(__FILE__, __LINE__, "Unable to locate gator.ko driver:\n >>> gator.ko should be co-located with gatord in the same directory\n >>> OR insmod gator.ko prior to launching gatord"); + + if (access(location, F_OK) == -1) { + logg->logError(__FILE__, __LINE__, "Unable to locate gator.ko driver:\n >>> gator.ko should be co-located with gatord in the same directory\n >>> OR insmod gator.ko prior to launching gatord\n >>> OR specify the location of gator.ko on the command line"); handleException(); } // Load driver - strcpy(command, "insmod "); - util->getApplicationFullPath(&command[7], sizeof(command) - 64); // allow some buffer space - strcat(command, "gator.ko >/dev/null 2>&1"); - + snprintf(command, sizeof(command), "insmod %s >/dev/null 2>&1", location); if (system(command) != 0) { logg->logMessage("Unable to load gator.ko driver with command: %s", command); logg->logError(__FILE__, __LINE__, "Unable to load (insmod) gator.ko driver:\n >>> gator.ko must be built against the current kernel version & configuration\n >>> See dmesg for more details"); @@ -163,11 +179,14 @@ int setupFilesystem() { } int shutdownFilesystem() { - if (driverMountedAtStart == false) + if (driverMountedAtStart == false) { umount("/dev/gator"); - if (driverRunningAtStart == false) - if (system("rmmod gator >/dev/null 2>&1") != 0) + } + if (driverRunningAtStart == false) { + if (system("rmmod gator >/dev/null 2>&1") != 0) { return -1; + } + } return 0; // success } @@ -175,29 +194,38 @@ int shutdownFilesystem() { struct cmdline_t parseCommandLine(int argc, char** argv) { struct cmdline_t cmdline; cmdline.port = 8080; - cmdline.sessionXML = NULL; + cmdline.module = NULL; int c; - while ((c = getopt (argc, argv, "hvp:s:c:")) != -1) { + while ((c = getopt(argc, argv, "hvp:s:c:e:m:")) != -1) { switch(c) { + case 'c': + gSessionData->mConfigurationXMLPath = optarg; + break; + case 'e': + gSessionData->mEventsXMLPath = optarg; + break; + case 'm': + cmdline.module = optarg; + break; case 'p': cmdline.port = strtol(optarg, NULL, 10); break; case 's': - cmdline.sessionXML = optarg; - break; - case 'c': - gSessionData->configurationXMLPath = optarg; + gSessionData->mSessionXMLPath = optarg; break; case 'h': case '?': logg->logError(__FILE__, __LINE__, "Streamline gatord version %d. All parameters are optional:\n" + "-c config_xml\tpath and filename of the configuration.xml to use\n" + "-e events_xml\tpath and filename of the events.xml to use\n" + "-h\t\tthis help page\n" + "-m module\tpath and filename of gator.ko\n" "-p port_number\tport upon which the server listens; default is 8080\n" "-s session_xml\tpath and filename of a session xml used for local capture\n" - "-c config_xml\tpath and filename of the configuration.xml to use\n" "-v\t\tversion information\n" - "-h\t\tthis help page\n", PROTOCOL_VERSION); + , PROTOCOL_VERSION); handleException(); break; case 'v': @@ -208,7 +236,7 @@ struct cmdline_t parseCommandLine(int argc, char** argv) { } // Error checking - if (cmdline.port != 8080 && cmdline.sessionXML != NULL) { + if (cmdline.port != 8080 && gSessionData->mSessionXMLPath != NULL) { logg->logError(__FILE__, __LINE__, "Only a port or a session xml can be specified, not both"); handleException(); } @@ -222,7 +250,7 @@ struct cmdline_t parseCommandLine(int argc, char** argv) { } // Gator data flow: collector -> collector fifo -> sender -int main(int argc, char** argv, char *envp[]) { +int main(int argc, char** argv, char* envp[]) { gSessionData = new SessionData(); // Global data class logg = new Logging(DEBUG); // Set up global thread-safe logging util = new OlyUtility(); // Set up global utility class @@ -235,8 +263,9 @@ int main(int argc, char** argv, char *envp[]) { signal(SIGABRT, handler); // Set to high priority - if (setpriority(PRIO_PROCESS, syscall(__NR_gettid), -19) == -1) + if (setpriority(PRIO_PROCESS, syscall(__NR_gettid), -19) == -1) { logg->logMessage("setpriority() failed"); + } // Initialize session data gSessionData->initialize(); @@ -245,7 +274,7 @@ int main(int argc, char** argv, char *envp[]) { struct cmdline_t cmdline = parseCommandLine(argc, argv); // Call before setting up the SIGCHLD handler, as system() spawns child processes - setupFilesystem(); + setupFilesystem(cmdline.module); // Handle child exit codes signal(SIGCHLD, child_exit); @@ -255,8 +284,8 @@ int main(int argc, char** argv, char *envp[]) { signal(SIGPIPE, SIG_IGN); // If the command line argument is a session xml file, no need to open a socket - if (cmdline.sessionXML) { - child = new Child(cmdline.sessionXML); + if (gSessionData->mSessionXMLPath) { + child = new Child(); child->run(); delete child; } else { diff --git a/daemon/mxml/COPYING b/daemon/mxml/COPYING new file mode 100644 index 0000000..4d0aa78 --- /dev/null +++ b/daemon/mxml/COPYING @@ -0,0 +1,507 @@ + Mini-XML License + September 18, 2010 + + +The Mini-XML library and included programs are provided under the +terms of the GNU Library General Public License version 2 (LGPL2) +with the following exceptions: + + 1. Static linking of applications to the Mini-XML library +does not constitute a derivative work and does not require +the author to provide source code for the application, use +the shared Mini-XML libraries, or link their applications +against a user-supplied version of Mini-XML. + +If you link the application to a modified version of +Mini-XML, then the changes to Mini-XML must be provided +under the terms of the LGPL2 in sections 1, 2, and 4. + + 2. You do not have to provide a copy of the Mini-XML license +with programs that are linked to the Mini-XML library, nor +do you have to identify the Mini-XML license in your +program or documentation as required by section 6 of the +LGPL2. + + + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + [This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/daemon/mxml/config.h b/daemon/mxml/config.h new file mode 100644 index 0000000..1f59ba3 --- /dev/null +++ b/daemon/mxml/config.h @@ -0,0 +1,96 @@ +/* config.h. Generated from config.h.in by configure. */ +/* + * "$Id: config.h.in 408 2010-09-19 05:26:46Z mike $" + * + * Configuration file for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2010 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + */ + +/* + * Include necessary headers... + */ + +#include +#include +#include +#include +#include + + +/* + * Version number... + */ + +#define MXML_VERSION "Mini-XML v2.7" + + +/* + * Inline function support... + */ + +#define inline + + +/* + * Long long support... + */ + +#define HAVE_LONG_LONG 1 + + +/* + * Do we have the snprintf() and vsnprintf() functions? + */ + +#define HAVE_SNPRINTF 1 +#define HAVE_VSNPRINTF 1 + + +/* + * Do we have the strXXX() functions? + */ + +#define HAVE_STRDUP 1 + + +/* + * Do we have threading support? + */ + +#define HAVE_PTHREAD_H 1 + + +/* + * Define prototypes for string functions as needed... + */ + +# ifndef HAVE_STRDUP +extern char *_mxml_strdup(const char *); +# define strdup _mxml_strdup +# endif /* !HAVE_STRDUP */ + +extern char *_mxml_strdupf(const char *, ...); +extern char *_mxml_vstrdupf(const char *, va_list); + +# ifndef HAVE_SNPRINTF +extern int _mxml_snprintf(char *, size_t, const char *, ...); +# define snprintf _mxml_snprintf +# endif /* !HAVE_SNPRINTF */ + +# ifndef HAVE_VSNPRINTF +extern int _mxml_vsnprintf(char *, size_t, const char *, va_list); +# define vsnprintf _mxml_vsnprintf +# endif /* !HAVE_VSNPRINTF */ + +/* + * End of "$Id: config.h.in 408 2010-09-19 05:26:46Z mike $". + */ diff --git a/daemon/mxml/mxml-attr.c b/daemon/mxml/mxml-attr.c new file mode 100644 index 0000000..c9950f5 --- /dev/null +++ b/daemon/mxml/mxml-attr.c @@ -0,0 +1,319 @@ +/* + * "$Id: mxml-attr.c 408 2010-09-19 05:26:46Z mike $" + * + * Attribute support code for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2010 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + * mxmlElementDeleteAttr() - Delete an attribute. + * mxmlElementGetAttr() - Get an attribute. + * mxmlElementSetAttr() - Set an attribute. + * mxmlElementSetAttrf() - Set an attribute with a formatted value. + * mxml_set_attr() - Set or add an attribute name/value pair. + */ + +/* + * Include necessary headers... + */ + +#include "config.h" +#include "mxml.h" + + +/* + * Local functions... + */ + +static int mxml_set_attr(mxml_node_t *node, const char *name, + char *value); + + +/* + * 'mxmlElementDeleteAttr()' - Delete an attribute. + * + * @since Mini-XML 2.4@ + */ + +void +mxmlElementDeleteAttr(mxml_node_t *node,/* I - Element */ + const char *name)/* I - Attribute name */ +{ + int i; /* Looping var */ + mxml_attr_t *attr; /* Cirrent attribute */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlElementDeleteAttr(node=%p, name=\"%s\")\n", + node, name ? name : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!node || node->type != MXML_ELEMENT || !name) + return; + + /* + * Look for the attribute... + */ + + for (i = node->value.element.num_attrs, attr = node->value.element.attrs; + i > 0; + i --, attr ++) + { +#ifdef DEBUG + printf(" %s=\"%s\"\n", attr->name, attr->value); +#endif /* DEBUG */ + + if (!strcmp(attr->name, name)) + { + /* + * Delete this attribute... + */ + + free(attr->name); + free(attr->value); + + i --; + if (i > 0) + memmove(attr, attr + 1, i * sizeof(mxml_attr_t)); + + node->value.element.num_attrs --; + return; + } + } +} + + +/* + * 'mxmlElementGetAttr()' - Get an attribute. + * + * This function returns NULL if the node is not an element or the + * named attribute does not exist. + */ + +const char * /* O - Attribute value or NULL */ +mxmlElementGetAttr(mxml_node_t *node, /* I - Element node */ + const char *name) /* I - Name of attribute */ +{ + int i; /* Looping var */ + mxml_attr_t *attr; /* Cirrent attribute */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlElementGetAttr(node=%p, name=\"%s\")\n", + node, name ? name : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!node || node->type != MXML_ELEMENT || !name) + return (NULL); + + /* + * Look for the attribute... + */ + + for (i = node->value.element.num_attrs, attr = node->value.element.attrs; + i > 0; + i --, attr ++) + { +#ifdef DEBUG + printf(" %s=\"%s\"\n", attr->name, attr->value); +#endif /* DEBUG */ + + if (!strcmp(attr->name, name)) + { +#ifdef DEBUG + printf(" Returning \"%s\"!\n", attr->value); +#endif /* DEBUG */ + return (attr->value); + } + } + + /* + * Didn't find attribute, so return NULL... + */ + +#ifdef DEBUG + puts(" Returning NULL!\n"); +#endif /* DEBUG */ + + return (NULL); +} + + +/* + * 'mxmlElementSetAttr()' - Set an attribute. + * + * If the named attribute already exists, the value of the attribute + * is replaced by the new string value. The string value is copied + * into the element node. This function does nothing if the node is + * not an element. + */ + +void +mxmlElementSetAttr(mxml_node_t *node, /* I - Element node */ + const char *name, /* I - Name of attribute */ + const char *value) /* I - Attribute value */ +{ + char *valuec; /* Copy of value */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlElementSetAttr(node=%p, name=\"%s\", value=\"%s\")\n", + node, name ? name : "(null)", value ? value : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!node || node->type != MXML_ELEMENT || !name) + return; + + if (value) + valuec = strdup(value); + else + valuec = NULL; + + if (mxml_set_attr(node, name, valuec)) + free(valuec); +} + + +/* + * 'mxmlElementSetAttrf()' - Set an attribute with a formatted value. + * + * If the named attribute already exists, the value of the attribute + * is replaced by the new formatted string. The formatted string value is + * copied into the element node. This function does nothing if the node + * is not an element. + * + * @since Mini-XML 2.3@ + */ + +void +mxmlElementSetAttrf(mxml_node_t *node, /* I - Element node */ + const char *name, /* I - Name of attribute */ + const char *format,/* I - Printf-style attribute value */ + ...) /* I - Additional arguments as needed */ +{ + va_list ap; /* Argument pointer */ + char *value; /* Value */ + + +#ifdef DEBUG + fprintf(stderr, + "mxmlElementSetAttrf(node=%p, name=\"%s\", format=\"%s\", ...)\n", + node, name ? name : "(null)", format ? format : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!node || node->type != MXML_ELEMENT || !name || !format) + return; + + /* + * Format the value... + */ + + va_start(ap, format); + value = _mxml_vstrdupf(format, ap); + va_end(ap); + + if (!value) + mxml_error("Unable to allocate memory for attribute '%s' in element %s!", + name, node->value.element.name); + else if (mxml_set_attr(node, name, value)) + free(value); +} + + +/* + * 'mxml_set_attr()' - Set or add an attribute name/value pair. + */ + +static int /* O - 0 on success, -1 on failure */ +mxml_set_attr(mxml_node_t *node, /* I - Element node */ + const char *name, /* I - Attribute name */ + char *value) /* I - Attribute value */ +{ + int i; /* Looping var */ + mxml_attr_t *attr; /* New attribute */ + + + /* + * Look for the attribute... + */ + + for (i = node->value.element.num_attrs, attr = node->value.element.attrs; + i > 0; + i --, attr ++) + if (!strcmp(attr->name, name)) + { + /* + * Free the old value as needed... + */ + + if (attr->value) + free(attr->value); + + attr->value = value; + + return (0); + } + + /* + * Add a new attribute... + */ + + if (node->value.element.num_attrs == 0) + attr = malloc(sizeof(mxml_attr_t)); + else + attr = realloc(node->value.element.attrs, + (node->value.element.num_attrs + 1) * sizeof(mxml_attr_t)); + + if (!attr) + { + mxml_error("Unable to allocate memory for attribute '%s' in element %s!", + name, node->value.element.name); + return (-1); + } + + node->value.element.attrs = attr; + attr += node->value.element.num_attrs; + + if ((attr->name = strdup(name)) == NULL) + { + mxml_error("Unable to allocate memory for attribute '%s' in element %s!", + name, node->value.element.name); + return (-1); + } + + attr->value = value; + + node->value.element.num_attrs ++; + + return (0); +} + + +/* + * End of "$Id: mxml-attr.c 408 2010-09-19 05:26:46Z mike $". + */ diff --git a/daemon/mxml/mxml-entity.c b/daemon/mxml/mxml-entity.c new file mode 100644 index 0000000..c5c9f61 --- /dev/null +++ b/daemon/mxml/mxml-entity.c @@ -0,0 +1,460 @@ +/* + * "$Id: mxml-entity.c 408 2010-09-19 05:26:46Z mike $" + * + * Character entity support code for Mini-XML, a small XML-like + * file parsing library. + * + * Copyright 2003-2010 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + * mxmlEntityAddCallback() - Add a callback to convert entities to + * Unicode. + * mxmlEntityGetName() - Get the name that corresponds to the + * character value. + * mxmlEntityGetValue() - Get the character corresponding to a named + * entity. + * mxmlEntityRemoveCallback() - Remove a callback. + * _mxml_entity_cb() - Lookup standard (X)HTML entities. + */ + +/* + * Include necessary headers... + */ + +#include "mxml-private.h" + + +/* + * 'mxmlEntityAddCallback()' - Add a callback to convert entities to Unicode. + */ + +int /* O - 0 on success, -1 on failure */ +mxmlEntityAddCallback( + mxml_entity_cb_t cb) /* I - Callback function to add */ +{ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + if (global->num_entity_cbs < (int)(sizeof(global->entity_cbs) / sizeof(global->entity_cbs[0]))) + { + global->entity_cbs[global->num_entity_cbs] = cb; + global->num_entity_cbs ++; + + return (0); + } + else + { + mxml_error("Unable to add entity callback!"); + + return (-1); + } +} + + +/* + * 'mxmlEntityGetName()' - Get the name that corresponds to the character value. + * + * If val does not need to be represented by a named entity, NULL is returned. + */ + +const char * /* O - Entity name or NULL */ +mxmlEntityGetName(int val) /* I - Character value */ +{ + switch (val) + { + case '&' : + return ("amp"); + + case '<' : + return ("lt"); + + case '>' : + return ("gt"); + + case '\"' : + return ("quot"); + + default : + return (NULL); + } +} + + +/* + * 'mxmlEntityGetValue()' - Get the character corresponding to a named entity. + * + * The entity name can also be a numeric constant. -1 is returned if the + * name is not known. + */ + +int /* O - Character value or -1 on error */ +mxmlEntityGetValue(const char *name) /* I - Entity name */ +{ + int i; /* Looping var */ + int ch; /* Character value */ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + for (i = 0; i < global->num_entity_cbs; i ++) + if ((ch = (global->entity_cbs[i])(name)) >= 0) + return (ch); + + return (-1); +} + + +/* + * 'mxmlEntityRemoveCallback()' - Remove a callback. + */ + +void +mxmlEntityRemoveCallback( + mxml_entity_cb_t cb) /* I - Callback function to remove */ +{ + int i; /* Looping var */ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + for (i = 0; i < global->num_entity_cbs; i ++) + if (cb == global->entity_cbs[i]) + { + /* + * Remove the callback... + */ + + global->num_entity_cbs --; + + if (i < global->num_entity_cbs) + memmove(global->entity_cbs + i, global->entity_cbs + i + 1, + (global->num_entity_cbs - i) * sizeof(global->entity_cbs[0])); + + return; + } +} + + +/* + * '_mxml_entity_cb()' - Lookup standard (X)HTML entities. + */ + +int /* O - Unicode value or -1 */ +_mxml_entity_cb(const char *name) /* I - Entity name */ +{ + int diff, /* Difference between names */ + current, /* Current entity in search */ + first, /* First entity in search */ + last; /* Last entity in search */ + static const struct + { + const char *name; /* Entity name */ + int val; /* Character value */ + } entities[] = + { + { "AElig", 198 }, + { "Aacute", 193 }, + { "Acirc", 194 }, + { "Agrave", 192 }, + { "Alpha", 913 }, + { "Aring", 197 }, + { "Atilde", 195 }, + { "Auml", 196 }, + { "Beta", 914 }, + { "Ccedil", 199 }, + { "Chi", 935 }, + { "Dagger", 8225 }, + { "Delta", 916 }, + { "Dstrok", 208 }, + { "ETH", 208 }, + { "Eacute", 201 }, + { "Ecirc", 202 }, + { "Egrave", 200 }, + { "Epsilon", 917 }, + { "Eta", 919 }, + { "Euml", 203 }, + { "Gamma", 915 }, + { "Iacute", 205 }, + { "Icirc", 206 }, + { "Igrave", 204 }, + { "Iota", 921 }, + { "Iuml", 207 }, + { "Kappa", 922 }, + { "Lambda", 923 }, + { "Mu", 924 }, + { "Ntilde", 209 }, + { "Nu", 925 }, + { "OElig", 338 }, + { "Oacute", 211 }, + { "Ocirc", 212 }, + { "Ograve", 210 }, + { "Omega", 937 }, + { "Omicron", 927 }, + { "Oslash", 216 }, + { "Otilde", 213 }, + { "Ouml", 214 }, + { "Phi", 934 }, + { "Pi", 928 }, + { "Prime", 8243 }, + { "Psi", 936 }, + { "Rho", 929 }, + { "Scaron", 352 }, + { "Sigma", 931 }, + { "THORN", 222 }, + { "Tau", 932 }, + { "Theta", 920 }, + { "Uacute", 218 }, + { "Ucirc", 219 }, + { "Ugrave", 217 }, + { "Upsilon", 933 }, + { "Uuml", 220 }, + { "Xi", 926 }, + { "Yacute", 221 }, + { "Yuml", 376 }, + { "Zeta", 918 }, + { "aacute", 225 }, + { "acirc", 226 }, + { "acute", 180 }, + { "aelig", 230 }, + { "agrave", 224 }, + { "alefsym", 8501 }, + { "alpha", 945 }, + { "amp", '&' }, + { "and", 8743 }, + { "ang", 8736 }, + { "apos", '\'' }, + { "aring", 229 }, + { "asymp", 8776 }, + { "atilde", 227 }, + { "auml", 228 }, + { "bdquo", 8222 }, + { "beta", 946 }, + { "brkbar", 166 }, + { "brvbar", 166 }, + { "bull", 8226 }, + { "cap", 8745 }, + { "ccedil", 231 }, + { "cedil", 184 }, + { "cent", 162 }, + { "chi", 967 }, + { "circ", 710 }, + { "clubs", 9827 }, + { "cong", 8773 }, + { "copy", 169 }, + { "crarr", 8629 }, + { "cup", 8746 }, + { "curren", 164 }, + { "dArr", 8659 }, + { "dagger", 8224 }, + { "darr", 8595 }, + { "deg", 176 }, + { "delta", 948 }, + { "diams", 9830 }, + { "die", 168 }, + { "divide", 247 }, + { "eacute", 233 }, + { "ecirc", 234 }, + { "egrave", 232 }, + { "empty", 8709 }, + { "emsp", 8195 }, + { "ensp", 8194 }, + { "epsilon", 949 }, + { "equiv", 8801 }, + { "eta", 951 }, + { "eth", 240 }, + { "euml", 235 }, + { "euro", 8364 }, + { "exist", 8707 }, + { "fnof", 402 }, + { "forall", 8704 }, + { "frac12", 189 }, + { "frac14", 188 }, + { "frac34", 190 }, + { "frasl", 8260 }, + { "gamma", 947 }, + { "ge", 8805 }, + { "gt", '>' }, + { "hArr", 8660 }, + { "harr", 8596 }, + { "hearts", 9829 }, + { "hellip", 8230 }, + { "hibar", 175 }, + { "iacute", 237 }, + { "icirc", 238 }, + { "iexcl", 161 }, + { "igrave", 236 }, + { "image", 8465 }, + { "infin", 8734 }, + { "int", 8747 }, + { "iota", 953 }, + { "iquest", 191 }, + { "isin", 8712 }, + { "iuml", 239 }, + { "kappa", 954 }, + { "lArr", 8656 }, + { "lambda", 955 }, + { "lang", 9001 }, + { "laquo", 171 }, + { "larr", 8592 }, + { "lceil", 8968 }, + { "ldquo", 8220 }, + { "le", 8804 }, + { "lfloor", 8970 }, + { "lowast", 8727 }, + { "loz", 9674 }, + { "lrm", 8206 }, + { "lsaquo", 8249 }, + { "lsquo", 8216 }, + { "lt", '<' }, + { "macr", 175 }, + { "mdash", 8212 }, + { "micro", 181 }, + { "middot", 183 }, + { "minus", 8722 }, + { "mu", 956 }, + { "nabla", 8711 }, + { "nbsp", 160 }, + { "ndash", 8211 }, + { "ne", 8800 }, + { "ni", 8715 }, + { "not", 172 }, + { "notin", 8713 }, + { "nsub", 8836 }, + { "ntilde", 241 }, + { "nu", 957 }, + { "oacute", 243 }, + { "ocirc", 244 }, + { "oelig", 339 }, + { "ograve", 242 }, + { "oline", 8254 }, + { "omega", 969 }, + { "omicron", 959 }, + { "oplus", 8853 }, + { "or", 8744 }, + { "ordf", 170 }, + { "ordm", 186 }, + { "oslash", 248 }, + { "otilde", 245 }, + { "otimes", 8855 }, + { "ouml", 246 }, + { "para", 182 }, + { "part", 8706 }, + { "permil", 8240 }, + { "perp", 8869 }, + { "phi", 966 }, + { "pi", 960 }, + { "piv", 982 }, + { "plusmn", 177 }, + { "pound", 163 }, + { "prime", 8242 }, + { "prod", 8719 }, + { "prop", 8733 }, + { "psi", 968 }, + { "quot", '\"' }, + { "rArr", 8658 }, + { "radic", 8730 }, + { "rang", 9002 }, + { "raquo", 187 }, + { "rarr", 8594 }, + { "rceil", 8969 }, + { "rdquo", 8221 }, + { "real", 8476 }, + { "reg", 174 }, + { "rfloor", 8971 }, + { "rho", 961 }, + { "rlm", 8207 }, + { "rsaquo", 8250 }, + { "rsquo", 8217 }, + { "sbquo", 8218 }, + { "scaron", 353 }, + { "sdot", 8901 }, + { "sect", 167 }, + { "shy", 173 }, + { "sigma", 963 }, + { "sigmaf", 962 }, + { "sim", 8764 }, + { "spades", 9824 }, + { "sub", 8834 }, + { "sube", 8838 }, + { "sum", 8721 }, + { "sup", 8835 }, + { "sup1", 185 }, + { "sup2", 178 }, + { "sup3", 179 }, + { "supe", 8839 }, + { "szlig", 223 }, + { "tau", 964 }, + { "there4", 8756 }, + { "theta", 952 }, + { "thetasym", 977 }, + { "thinsp", 8201 }, + { "thorn", 254 }, + { "tilde", 732 }, + { "times", 215 }, + { "trade", 8482 }, + { "uArr", 8657 }, + { "uacute", 250 }, + { "uarr", 8593 }, + { "ucirc", 251 }, + { "ugrave", 249 }, + { "uml", 168 }, + { "upsih", 978 }, + { "upsilon", 965 }, + { "uuml", 252 }, + { "weierp", 8472 }, + { "xi", 958 }, + { "yacute", 253 }, + { "yen", 165 }, + { "yuml", 255 }, + { "zeta", 950 }, + { "zwj", 8205 }, + { "zwnj", 8204 } + }; + + + /* + * Do a binary search for the named entity... + */ + + first = 0; + last = (int)(sizeof(entities) / sizeof(entities[0]) - 1); + + while ((last - first) > 1) + { + current = (first + last) / 2; + + if ((diff = strcmp(name, entities[current].name)) == 0) + return (entities[current].val); + else if (diff < 0) + last = current; + else + first = current; + } + + /* + * If we get here, there is a small chance that there is still + * a match; check first and last... + */ + + if (!strcmp(name, entities[first].name)) + return (entities[first].val); + else if (!strcmp(name, entities[last].name)) + return (entities[last].val); + else + return (-1); +} + + +/* + * End of "$Id: mxml-entity.c 408 2010-09-19 05:26:46Z mike $". + */ diff --git a/daemon/mxml/mxml-file.c b/daemon/mxml/mxml-file.c new file mode 100644 index 0000000..9927040 --- /dev/null +++ b/daemon/mxml/mxml-file.c @@ -0,0 +1,3080 @@ +/* + * "$Id: mxml-file.c 438 2011-03-24 05:47:51Z mike $" + * + * File loading code for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2011 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + * mxmlLoadFd() - Load a file descriptor into an XML node tree. + * mxmlLoadFile() - Load a file into an XML node tree. + * mxmlLoadString() - Load a string into an XML node tree. + * mxmlSaveAllocString() - Save an XML tree to an allocated string. + * mxmlSaveFd() - Save an XML tree to a file descriptor. + * mxmlSaveFile() - Save an XML tree to a file. + * mxmlSaveString() - Save an XML node tree to a string. + * mxmlSAXLoadFd() - Load a file descriptor into an XML node tree + * using a SAX callback. + * mxmlSAXLoadFile() - Load a file into an XML node tree + * using a SAX callback. + * mxmlSAXLoadString() - Load a string into an XML node tree + * using a SAX callback. + * mxmlSetCustomHandlers() - Set the handling functions for custom data. + * mxmlSetErrorCallback() - Set the error message callback. + * mxmlSetWrapMargin() - Set the wrap margin when saving XML data. + * mxml_add_char() - Add a character to a buffer, expanding as needed. + * mxml_fd_getc() - Read a character from a file descriptor. + * mxml_fd_putc() - Write a character to a file descriptor. + * mxml_fd_read() - Read a buffer of data from a file descriptor. + * mxml_fd_write() - Write a buffer of data to a file descriptor. + * mxml_file_getc() - Get a character from a file. + * mxml_file_putc() - Write a character to a file. + * mxml_get_entity() - Get the character corresponding to an entity... + * mxml_load_data() - Load data into an XML node tree. + * mxml_parse_element() - Parse an element for any attributes... + * mxml_string_getc() - Get a character from a string. + * mxml_string_putc() - Write a character to a string. + * mxml_write_name() - Write a name string. + * mxml_write_node() - Save an XML node to a file. + * mxml_write_string() - Write a string, escaping & and < as needed. + * mxml_write_ws() - Do whitespace callback... + */ + +/* + * Include necessary headers... + */ + +#ifndef WIN32 +# include +#endif /* !WIN32 */ +#include "mxml-private.h" + + +/* + * Character encoding... + */ + +#define ENCODE_UTF8 0 /* UTF-8 */ +#define ENCODE_UTF16BE 1 /* UTF-16 Big-Endian */ +#define ENCODE_UTF16LE 2 /* UTF-16 Little-Endian */ + + +/* + * Macro to test for a bad XML character... + */ + +#define mxml_bad_char(ch) ((ch) < ' ' && (ch) != '\n' && (ch) != '\r' && (ch) != '\t') + + +/* + * Types and structures... + */ + +typedef int (*_mxml_getc_cb_t)(void *, int *); +typedef int (*_mxml_putc_cb_t)(int, void *); + +typedef struct _mxml_fdbuf_s /**** File descriptor buffer ****/ +{ + int fd; /* File descriptor */ + unsigned char *current, /* Current position in buffer */ + *end, /* End of buffer */ + buffer[8192]; /* Character buffer */ +} _mxml_fdbuf_t; + + +/* + * Local functions... + */ + +static int mxml_add_char(int ch, char **ptr, char **buffer, + int *bufsize); +static int mxml_fd_getc(void *p, int *encoding); +static int mxml_fd_putc(int ch, void *p); +static int mxml_fd_read(_mxml_fdbuf_t *buf); +static int mxml_fd_write(_mxml_fdbuf_t *buf); +static int mxml_file_getc(void *p, int *encoding); +static int mxml_file_putc(int ch, void *p); +static int mxml_get_entity(mxml_node_t *parent, void *p, + int *encoding, + _mxml_getc_cb_t getc_cb); +static inline int mxml_isspace(int ch) + { + return (ch == ' ' || ch == '\t' || ch == '\r' || + ch == '\n'); + } +static mxml_node_t *mxml_load_data(mxml_node_t *top, void *p, + mxml_load_cb_t cb, + _mxml_getc_cb_t getc_cb, + mxml_sax_cb_t sax_cb, void *sax_data); +static int mxml_parse_element(mxml_node_t *node, void *p, + int *encoding, + _mxml_getc_cb_t getc_cb); +static int mxml_string_getc(void *p, int *encoding); +static int mxml_string_putc(int ch, void *p); +static int mxml_write_name(const char *s, void *p, + _mxml_putc_cb_t putc_cb); +static int mxml_write_node(mxml_node_t *node, void *p, + mxml_save_cb_t cb, int col, + _mxml_putc_cb_t putc_cb, + _mxml_global_t *global); +static int mxml_write_string(const char *s, void *p, + _mxml_putc_cb_t putc_cb); +static int mxml_write_ws(mxml_node_t *node, void *p, + mxml_save_cb_t cb, int ws, + int col, _mxml_putc_cb_t putc_cb); + + +/* + * 'mxmlLoadFd()' - Load a file descriptor into an XML node tree. + * + * The nodes in the specified file are added to the specified top node. + * If no top node is provided, the XML file MUST be well-formed with a + * single parent node like for the entire file. The callback + * function returns the value type that should be used for child nodes. + * If MXML_NO_CALLBACK is specified then all child nodes will be either + * MXML_ELEMENT or MXML_TEXT nodes. + * + * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, + * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading + * child nodes of the specified type. + */ + +mxml_node_t * /* O - First node or NULL if the file could not be read. */ +mxmlLoadFd(mxml_node_t *top, /* I - Top node */ + int fd, /* I - File descriptor to read from */ + mxml_load_cb_t cb) /* I - Callback function or MXML_NO_CALLBACK */ +{ + _mxml_fdbuf_t buf; /* File descriptor buffer */ + + + /* + * Initialize the file descriptor buffer... + */ + + buf.fd = fd; + buf.current = buf.buffer; + buf.end = buf.buffer; + + /* + * Read the XML data... + */ + + return (mxml_load_data(top, &buf, cb, mxml_fd_getc, MXML_NO_CALLBACK, NULL)); +} + + +/* + * 'mxmlLoadFile()' - Load a file into an XML node tree. + * + * The nodes in the specified file are added to the specified top node. + * If no top node is provided, the XML file MUST be well-formed with a + * single parent node like for the entire file. The callback + * function returns the value type that should be used for child nodes. + * If MXML_NO_CALLBACK is specified then all child nodes will be either + * MXML_ELEMENT or MXML_TEXT nodes. + * + * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, + * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading + * child nodes of the specified type. + */ + +mxml_node_t * /* O - First node or NULL if the file could not be read. */ +mxmlLoadFile(mxml_node_t *top, /* I - Top node */ + FILE *fp, /* I - File to read from */ + mxml_load_cb_t cb) /* I - Callback function or MXML_NO_CALLBACK */ +{ + /* + * Read the XML data... + */ + + return (mxml_load_data(top, fp, cb, mxml_file_getc, MXML_NO_CALLBACK, NULL)); +} + + +/* + * 'mxmlLoadString()' - Load a string into an XML node tree. + * + * The nodes in the specified string are added to the specified top node. + * If no top node is provided, the XML string MUST be well-formed with a + * single parent node like for the entire string. The callback + * function returns the value type that should be used for child nodes. + * If MXML_NO_CALLBACK is specified then all child nodes will be either + * MXML_ELEMENT or MXML_TEXT nodes. + * + * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, + * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading + * child nodes of the specified type. + */ + +mxml_node_t * /* O - First node or NULL if the string has errors. */ +mxmlLoadString(mxml_node_t *top, /* I - Top node */ + const char *s, /* I - String to load */ + mxml_load_cb_t cb) /* I - Callback function or MXML_NO_CALLBACK */ +{ + /* + * Read the XML data... + */ + + return (mxml_load_data(top, (void *)&s, cb, mxml_string_getc, MXML_NO_CALLBACK, + NULL)); +} + + +/* + * 'mxmlSaveAllocString()' - Save an XML tree to an allocated string. + * + * This function returns a pointer to a string containing the textual + * representation of the XML node tree. The string should be freed + * using the free() function when you are done with it. NULL is returned + * if the node would produce an empty string or if the string cannot be + * allocated. + * + * The callback argument specifies a function that returns a whitespace + * string or NULL before and after each element. If MXML_NO_CALLBACK + * is specified, whitespace will only be added before MXML_TEXT nodes + * with leading whitespace and before attribute names inside opening + * element tags. + */ + +char * /* O - Allocated string or NULL */ +mxmlSaveAllocString( + mxml_node_t *node, /* I - Node to write */ + mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */ +{ + int bytes; /* Required bytes */ + char buffer[8192]; /* Temporary buffer */ + char *s; /* Allocated string */ + + + /* + * Write the node to the temporary buffer... + */ + + bytes = mxmlSaveString(node, buffer, sizeof(buffer), cb); + + if (bytes <= 0) + return (NULL); + + if (bytes < (int)(sizeof(buffer) - 1)) + { + /* + * Node fit inside the buffer, so just duplicate that string and + * return... + */ + + return (strdup(buffer)); + } + + /* + * Allocate a buffer of the required size and save the node to the + * new buffer... + */ + + if ((s = malloc(bytes + 1)) == NULL) + return (NULL); + + mxmlSaveString(node, s, bytes + 1, cb); + + /* + * Return the allocated string... + */ + + return (s); +} + + +/* + * 'mxmlSaveFd()' - Save an XML tree to a file descriptor. + * + * The callback argument specifies a function that returns a whitespace + * string or NULL before and after each element. If MXML_NO_CALLBACK + * is specified, whitespace will only be added before MXML_TEXT nodes + * with leading whitespace and before attribute names inside opening + * element tags. + */ + +int /* O - 0 on success, -1 on error. */ +mxmlSaveFd(mxml_node_t *node, /* I - Node to write */ + int fd, /* I - File descriptor to write to */ + mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */ +{ + int col; /* Final column */ + _mxml_fdbuf_t buf; /* File descriptor buffer */ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + /* + * Initialize the file descriptor buffer... + */ + + buf.fd = fd; + buf.current = buf.buffer; + buf.end = buf.buffer + sizeof(buf.buffer); + + /* + * Write the node... + */ + + if ((col = mxml_write_node(node, &buf, cb, 0, mxml_fd_putc, global)) < 0) + return (-1); + + if (col > 0) + if (mxml_fd_putc('\n', &buf) < 0) + return (-1); + + /* + * Flush and return... + */ + + return (mxml_fd_write(&buf)); +} + + +/* + * 'mxmlSaveFile()' - Save an XML tree to a file. + * + * The callback argument specifies a function that returns a whitespace + * string or NULL before and after each element. If MXML_NO_CALLBACK + * is specified, whitespace will only be added before MXML_TEXT nodes + * with leading whitespace and before attribute names inside opening + * element tags. + */ + +int /* O - 0 on success, -1 on error. */ +mxmlSaveFile(mxml_node_t *node, /* I - Node to write */ + FILE *fp, /* I - File to write to */ + mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */ +{ + int col; /* Final column */ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + /* + * Write the node... + */ + + if ((col = mxml_write_node(node, fp, cb, 0, mxml_file_putc, global)) < 0) + return (-1); + + if (col > 0) + if (putc('\n', fp) < 0) + return (-1); + + /* + * Return 0 (success)... + */ + + return (0); +} + + +/* + * 'mxmlSaveString()' - Save an XML node tree to a string. + * + * This function returns the total number of bytes that would be + * required for the string but only copies (bufsize - 1) characters + * into the specified buffer. + * + * The callback argument specifies a function that returns a whitespace + * string or NULL before and after each element. If MXML_NO_CALLBACK + * is specified, whitespace will only be added before MXML_TEXT nodes + * with leading whitespace and before attribute names inside opening + * element tags. + */ + +int /* O - Size of string */ +mxmlSaveString(mxml_node_t *node, /* I - Node to write */ + char *buffer, /* I - String buffer */ + int bufsize, /* I - Size of string buffer */ + mxml_save_cb_t cb) /* I - Whitespace callback or MXML_NO_CALLBACK */ +{ + int col; /* Final column */ + char *ptr[2]; /* Pointers for putc_cb */ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + /* + * Write the node... + */ + + ptr[0] = buffer; + ptr[1] = buffer + bufsize; + + if ((col = mxml_write_node(node, ptr, cb, 0, mxml_string_putc, global)) < 0) + return (-1); + + if (col > 0) + mxml_string_putc('\n', ptr); + + /* + * Nul-terminate the buffer... + */ + + if (ptr[0] >= ptr[1]) + buffer[bufsize - 1] = '\0'; + else + ptr[0][0] = '\0'; + + /* + * Return the number of characters... + */ + + return (ptr[0] - buffer); +} + + +/* + * 'mxmlSAXLoadFd()' - Load a file descriptor into an XML node tree + * using a SAX callback. + * + * The nodes in the specified file are added to the specified top node. + * If no top node is provided, the XML file MUST be well-formed with a + * single parent node like for the entire file. The callback + * function returns the value type that should be used for child nodes. + * If MXML_NO_CALLBACK is specified then all child nodes will be either + * MXML_ELEMENT or MXML_TEXT nodes. + * + * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, + * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading + * child nodes of the specified type. + * + * The SAX callback must call mxmlRetain() for any nodes that need to + * be kept for later use. Otherwise, nodes are deleted when the parent + * node is closed or after each data, comment, CDATA, or directive node. + * + * @since Mini-XML 2.3@ + */ + +mxml_node_t * /* O - First node or NULL if the file could not be read. */ +mxmlSAXLoadFd(mxml_node_t *top, /* I - Top node */ + int fd, /* I - File descriptor to read from */ + mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */ + mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */ + void *sax_data) /* I - SAX user data */ +{ + _mxml_fdbuf_t buf; /* File descriptor buffer */ + + + /* + * Initialize the file descriptor buffer... + */ + + buf.fd = fd; + buf.current = buf.buffer; + buf.end = buf.buffer; + + /* + * Read the XML data... + */ + + return (mxml_load_data(top, &buf, cb, mxml_fd_getc, sax_cb, sax_data)); +} + + +/* + * 'mxmlSAXLoadFile()' - Load a file into an XML node tree + * using a SAX callback. + * + * The nodes in the specified file are added to the specified top node. + * If no top node is provided, the XML file MUST be well-formed with a + * single parent node like for the entire file. The callback + * function returns the value type that should be used for child nodes. + * If MXML_NO_CALLBACK is specified then all child nodes will be either + * MXML_ELEMENT or MXML_TEXT nodes. + * + * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, + * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading + * child nodes of the specified type. + * + * The SAX callback must call mxmlRetain() for any nodes that need to + * be kept for later use. Otherwise, nodes are deleted when the parent + * node is closed or after each data, comment, CDATA, or directive node. + * + * @since Mini-XML 2.3@ + */ + +mxml_node_t * /* O - First node or NULL if the file could not be read. */ +mxmlSAXLoadFile( + mxml_node_t *top, /* I - Top node */ + FILE *fp, /* I - File to read from */ + mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */ + mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */ + void *sax_data) /* I - SAX user data */ +{ + /* + * Read the XML data... + */ + + return (mxml_load_data(top, fp, cb, mxml_file_getc, sax_cb, sax_data)); +} + + +/* + * 'mxmlSAXLoadString()' - Load a string into an XML node tree + * using a SAX callback. + * + * The nodes in the specified string are added to the specified top node. + * If no top node is provided, the XML string MUST be well-formed with a + * single parent node like for the entire string. The callback + * function returns the value type that should be used for child nodes. + * If MXML_NO_CALLBACK is specified then all child nodes will be either + * MXML_ELEMENT or MXML_TEXT nodes. + * + * The constants MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, + * MXML_REAL_CALLBACK, and MXML_TEXT_CALLBACK are defined for loading + * child nodes of the specified type. + * + * The SAX callback must call mxmlRetain() for any nodes that need to + * be kept for later use. Otherwise, nodes are deleted when the parent + * node is closed or after each data, comment, CDATA, or directive node. + * + * @since Mini-XML 2.3@ + */ + +mxml_node_t * /* O - First node or NULL if the string has errors. */ +mxmlSAXLoadString( + mxml_node_t *top, /* I - Top node */ + const char *s, /* I - String to load */ + mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */ + mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */ + void *sax_data) /* I - SAX user data */ +{ + /* + * Read the XML data... + */ + + return (mxml_load_data(top, (void *)&s, cb, mxml_string_getc, sax_cb, sax_data)); +} + + +/* + * 'mxmlSetCustomHandlers()' - Set the handling functions for custom data. + * + * The load function accepts a node pointer and a data string and must + * return 0 on success and non-zero on error. + * + * The save function accepts a node pointer and must return a malloc'd + * string on success and NULL on error. + * + */ + +void +mxmlSetCustomHandlers( + mxml_custom_load_cb_t load, /* I - Load function */ + mxml_custom_save_cb_t save) /* I - Save function */ +{ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + global->custom_load_cb = load; + global->custom_save_cb = save; +} + + +/* + * 'mxmlSetErrorCallback()' - Set the error message callback. + */ + +void +mxmlSetErrorCallback(mxml_error_cb_t cb)/* I - Error callback function */ +{ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + global->error_cb = cb; +} + + +/* + * 'mxmlSetWrapMargin()' - Set the wrap margin when saving XML data. + * + * Wrapping is disabled when "column" is 0. + * + * @since Mini-XML 2.3@ + */ + +void +mxmlSetWrapMargin(int column) /* I - Column for wrapping, 0 to disable wrapping */ +{ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + global->wrap = column; +} + + +/* + * 'mxml_add_char()' - Add a character to a buffer, expanding as needed. + */ + +static int /* O - 0 on success, -1 on error */ +mxml_add_char(int ch, /* I - Character to add */ + char **bufptr, /* IO - Current position in buffer */ + char **buffer, /* IO - Current buffer */ + int *bufsize) /* IO - Current buffer size */ +{ + char *newbuffer; /* New buffer value */ + + + if (*bufptr >= (*buffer + *bufsize - 4)) + { + /* + * Increase the size of the buffer... + */ + + if (*bufsize < 1024) + (*bufsize) *= 2; + else + (*bufsize) += 1024; + + if ((newbuffer = realloc(*buffer, *bufsize)) == NULL) + { + free(*buffer); + + mxml_error("Unable to expand string buffer to %d bytes!", *bufsize); + + return (-1); + } + + *bufptr = newbuffer + (*bufptr - *buffer); + *buffer = newbuffer; + } + + if (ch < 0x80) + { + /* + * Single byte ASCII... + */ + + *(*bufptr)++ = ch; + } + else if (ch < 0x800) + { + /* + * Two-byte UTF-8... + */ + + *(*bufptr)++ = 0xc0 | (ch >> 6); + *(*bufptr)++ = 0x80 | (ch & 0x3f); + } + else if (ch < 0x10000) + { + /* + * Three-byte UTF-8... + */ + + *(*bufptr)++ = 0xe0 | (ch >> 12); + *(*bufptr)++ = 0x80 | ((ch >> 6) & 0x3f); + *(*bufptr)++ = 0x80 | (ch & 0x3f); + } + else + { + /* + * Four-byte UTF-8... + */ + + *(*bufptr)++ = 0xf0 | (ch >> 18); + *(*bufptr)++ = 0x80 | ((ch >> 12) & 0x3f); + *(*bufptr)++ = 0x80 | ((ch >> 6) & 0x3f); + *(*bufptr)++ = 0x80 | (ch & 0x3f); + } + + return (0); +} + + +/* + * 'mxml_fd_getc()' - Read a character from a file descriptor. + */ + +static int /* O - Character or EOF */ +mxml_fd_getc(void *p, /* I - File descriptor buffer */ + int *encoding) /* IO - Encoding */ +{ + _mxml_fdbuf_t *buf; /* File descriptor buffer */ + int ch, /* Current character */ + temp; /* Temporary character */ + + + /* + * Grab the next character in the buffer... + */ + + buf = (_mxml_fdbuf_t *)p; + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + ch = *(buf->current)++; + + switch (*encoding) + { + case ENCODE_UTF8 : + /* + * Got a UTF-8 character; convert UTF-8 to Unicode and return... + */ + + if (!(ch & 0x80)) + { +#if DEBUG > 1 + printf("mxml_fd_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } + + return (ch); + } + else if (ch == 0xfe) + { + /* + * UTF-16 big-endian BOM? + */ + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + ch = *(buf->current)++; + + if (ch != 0xff) + return (EOF); + + *encoding = ENCODE_UTF16BE; + + return (mxml_fd_getc(p, encoding)); + } + else if (ch == 0xff) + { + /* + * UTF-16 little-endian BOM? + */ + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + ch = *(buf->current)++; + + if (ch != 0xfe) + return (EOF); + + *encoding = ENCODE_UTF16LE; + + return (mxml_fd_getc(p, encoding)); + } + else if ((ch & 0xe0) == 0xc0) + { + /* + * Two-byte value... + */ + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + if ((temp & 0xc0) != 0x80) + return (EOF); + + ch = ((ch & 0x1f) << 6) | (temp & 0x3f); + + if (ch < 0x80) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + } + else if ((ch & 0xf0) == 0xe0) + { + /* + * Three-byte value... + */ + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + if ((temp & 0xc0) != 0x80) + return (EOF); + + ch = ((ch & 0x0f) << 6) | (temp & 0x3f); + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + if ((temp & 0xc0) != 0x80) + return (EOF); + + ch = (ch << 6) | (temp & 0x3f); + + if (ch < 0x800) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + + /* + * Ignore (strip) Byte Order Mark (BOM)... + */ + + if (ch == 0xfeff) + return (mxml_fd_getc(p, encoding)); + } + else if ((ch & 0xf8) == 0xf0) + { + /* + * Four-byte value... + */ + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + if ((temp & 0xc0) != 0x80) + return (EOF); + + ch = ((ch & 0x07) << 6) | (temp & 0x3f); + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + if ((temp & 0xc0) != 0x80) + return (EOF); + + ch = (ch << 6) | (temp & 0x3f); + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + if ((temp & 0xc0) != 0x80) + return (EOF); + + ch = (ch << 6) | (temp & 0x3f); + + if (ch < 0x10000) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + } + else + return (EOF); + break; + + case ENCODE_UTF16BE : + /* + * Read UTF-16 big-endian char... + */ + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + ch = (ch << 8) | temp; + + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } + else if (ch >= 0xd800 && ch <= 0xdbff) + { + /* + * Multi-word UTF-16 char... + */ + + int lch; + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + lch = *(buf->current)++; + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + lch = (lch << 8) | temp; + + if (lch < 0xdc00 || lch >= 0xdfff) + return (EOF); + + ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; + } + break; + + case ENCODE_UTF16LE : + /* + * Read UTF-16 little-endian char... + */ + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + ch |= (temp << 8); + + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } + else if (ch >= 0xd800 && ch <= 0xdbff) + { + /* + * Multi-word UTF-16 char... + */ + + int lch; + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + lch = *(buf->current)++; + + if (buf->current >= buf->end) + if (mxml_fd_read(buf) < 0) + return (EOF); + + temp = *(buf->current)++; + + lch |= (temp << 8); + + if (lch < 0xdc00 || lch >= 0xdfff) + return (EOF); + + ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; + } + break; + } + +#if DEBUG > 1 + printf("mxml_fd_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + return (ch); +} + + +/* + * 'mxml_fd_putc()' - Write a character to a file descriptor. + */ + +static int /* O - 0 on success, -1 on error */ +mxml_fd_putc(int ch, /* I - Character */ + void *p) /* I - File descriptor buffer */ +{ + _mxml_fdbuf_t *buf; /* File descriptor buffer */ + + + /* + * Flush the write buffer as needed... + */ + + buf = (_mxml_fdbuf_t *)p; + + if (buf->current >= buf->end) + if (mxml_fd_write(buf) < 0) + return (-1); + + *(buf->current)++ = ch; + + /* + * Return successfully... + */ + + return (0); +} + + +/* + * 'mxml_fd_read()' - Read a buffer of data from a file descriptor. + */ + +static int /* O - 0 on success, -1 on error */ +mxml_fd_read(_mxml_fdbuf_t *buf) /* I - File descriptor buffer */ +{ + int bytes; /* Bytes read... */ + + + /* + * Range check input... + */ + + if (!buf) + return (-1); + + /* + * Read from the file descriptor... + */ + + while ((bytes = read(buf->fd, buf->buffer, sizeof(buf->buffer))) < 0) +#ifdef EINTR + if (errno != EAGAIN && errno != EINTR) +#else + if (errno != EAGAIN) +#endif /* EINTR */ + return (-1); + + if (bytes == 0) + return (-1); + + /* + * Update the pointers and return success... + */ + + buf->current = buf->buffer; + buf->end = buf->buffer + bytes; + + return (0); +} + + +/* + * 'mxml_fd_write()' - Write a buffer of data to a file descriptor. + */ + +static int /* O - 0 on success, -1 on error */ +mxml_fd_write(_mxml_fdbuf_t *buf) /* I - File descriptor buffer */ +{ + int bytes; /* Bytes written */ + unsigned char *ptr; /* Pointer into buffer */ + + + /* + * Range check... + */ + + if (!buf) + return (-1); + + /* + * Return 0 if there is nothing to write... + */ + + if (buf->current == buf->buffer) + return (0); + + /* + * Loop until we have written everything... + */ + + for (ptr = buf->buffer; ptr < buf->current; ptr += bytes) + if ((bytes = write(buf->fd, ptr, buf->current - ptr)) < 0) + return (-1); + + /* + * All done, reset pointers and return success... + */ + + buf->current = buf->buffer; + + return (0); +} + + +/* + * 'mxml_file_getc()' - Get a character from a file. + */ + +static int /* O - Character or EOF */ +mxml_file_getc(void *p, /* I - Pointer to file */ + int *encoding) /* IO - Encoding */ +{ + int ch, /* Character from file */ + temp; /* Temporary character */ + FILE *fp; /* Pointer to file */ + + + /* + * Read a character from the file and see if it is EOF or ASCII... + */ + + fp = (FILE *)p; + ch = getc(fp); + + if (ch == EOF) + return (EOF); + + switch (*encoding) + { + case ENCODE_UTF8 : + /* + * Got a UTF-8 character; convert UTF-8 to Unicode and return... + */ + + if (!(ch & 0x80)) + { + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } + +#if DEBUG > 1 + printf("mxml_file_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + return (ch); + } + else if (ch == 0xfe) + { + /* + * UTF-16 big-endian BOM? + */ + + ch = getc(fp); + if (ch != 0xff) + return (EOF); + + *encoding = ENCODE_UTF16BE; + + return (mxml_file_getc(p, encoding)); + } + else if (ch == 0xff) + { + /* + * UTF-16 little-endian BOM? + */ + + ch = getc(fp); + if (ch != 0xfe) + return (EOF); + + *encoding = ENCODE_UTF16LE; + + return (mxml_file_getc(p, encoding)); + } + else if ((ch & 0xe0) == 0xc0) + { + /* + * Two-byte value... + */ + + if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) + return (EOF); + + ch = ((ch & 0x1f) << 6) | (temp & 0x3f); + + if (ch < 0x80) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + } + else if ((ch & 0xf0) == 0xe0) + { + /* + * Three-byte value... + */ + + if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) + return (EOF); + + ch = ((ch & 0x0f) << 6) | (temp & 0x3f); + + if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) + return (EOF); + + ch = (ch << 6) | (temp & 0x3f); + + if (ch < 0x800) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + + /* + * Ignore (strip) Byte Order Mark (BOM)... + */ + + if (ch == 0xfeff) + return (mxml_file_getc(p, encoding)); + } + else if ((ch & 0xf8) == 0xf0) + { + /* + * Four-byte value... + */ + + if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) + return (EOF); + + ch = ((ch & 0x07) << 6) | (temp & 0x3f); + + if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) + return (EOF); + + ch = (ch << 6) | (temp & 0x3f); + + if ((temp = getc(fp)) == EOF || (temp & 0xc0) != 0x80) + return (EOF); + + ch = (ch << 6) | (temp & 0x3f); + + if (ch < 0x10000) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + } + else + return (EOF); + break; + + case ENCODE_UTF16BE : + /* + * Read UTF-16 big-endian char... + */ + + ch = (ch << 8) | getc(fp); + + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } + else if (ch >= 0xd800 && ch <= 0xdbff) + { + /* + * Multi-word UTF-16 char... + */ + + int lch = (getc(fp) << 8) | getc(fp); + + if (lch < 0xdc00 || lch >= 0xdfff) + return (EOF); + + ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; + } + break; + + case ENCODE_UTF16LE : + /* + * Read UTF-16 little-endian char... + */ + + ch |= (getc(fp) << 8); + + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } + else if (ch >= 0xd800 && ch <= 0xdbff) + { + /* + * Multi-word UTF-16 char... + */ + + int lch = getc(fp) | (getc(fp) << 8); + + if (lch < 0xdc00 || lch >= 0xdfff) + return (EOF); + + ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; + } + break; + } + +#if DEBUG > 1 + printf("mxml_file_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + return (ch); +} + + +/* + * 'mxml_file_putc()' - Write a character to a file. + */ + +static int /* O - 0 on success, -1 on failure */ +mxml_file_putc(int ch, /* I - Character to write */ + void *p) /* I - Pointer to file */ +{ + return (putc(ch, (FILE *)p) == EOF ? -1 : 0); +} + + +/* + * 'mxml_get_entity()' - Get the character corresponding to an entity... + */ + +static int /* O - Character value or EOF on error */ +mxml_get_entity(mxml_node_t *parent, /* I - Parent node */ + void *p, /* I - Pointer to source */ + int *encoding, /* IO - Character encoding */ + int (*getc_cb)(void *, int *)) + /* I - Get character function */ +{ + int ch; /* Current character */ + char entity[64], /* Entity string */ + *entptr; /* Pointer into entity */ + + + entptr = entity; + + while ((ch = (*getc_cb)(p, encoding)) != EOF) + if (ch > 126 || (!isalnum(ch) && ch != '#')) + break; + else if (entptr < (entity + sizeof(entity) - 1)) + *entptr++ = ch; + else + { + mxml_error("Entity name too long under parent <%s>!", + parent ? parent->value.element.name : "null"); + break; + } + + *entptr = '\0'; + + if (ch != ';') + { + mxml_error("Character entity \"%s\" not terminated under parent <%s>!", + entity, parent ? parent->value.element.name : "null"); + return (EOF); + } + + if (entity[0] == '#') + { + if (entity[1] == 'x') + ch = strtol(entity + 2, NULL, 16); + else + ch = strtol(entity + 1, NULL, 10); + } + else if ((ch = mxmlEntityGetValue(entity)) < 0) + mxml_error("Entity name \"%s;\" not supported under parent <%s>!", + entity, parent ? parent->value.element.name : "null"); + + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x under parent <%s> not allowed by XML standard!", + ch, parent ? parent->value.element.name : "null"); + return (EOF); + } + + return (ch); +} + + +/* + * 'mxml_load_data()' - Load data into an XML node tree. + */ + +static mxml_node_t * /* O - First node or NULL if the file could not be read. */ +mxml_load_data( + mxml_node_t *top, /* I - Top node */ + void *p, /* I - Pointer to data */ + mxml_load_cb_t cb, /* I - Callback function or MXML_NO_CALLBACK */ + _mxml_getc_cb_t getc_cb, /* I - Read function */ + mxml_sax_cb_t sax_cb, /* I - SAX callback or MXML_NO_CALLBACK */ + void *sax_data) /* I - SAX user data */ +{ + mxml_node_t *node, /* Current node */ + *first, /* First node added */ + *parent; /* Current parent node */ + int ch, /* Character from file */ + whitespace; /* Non-zero if whitespace seen */ + char *buffer, /* String buffer */ + *bufptr; /* Pointer into buffer */ + int bufsize; /* Size of buffer */ + mxml_type_t type; /* Current node type */ + int encoding; /* Character encoding */ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + static const char * const types[] = /* Type strings... */ + { + "MXML_ELEMENT", /* XML element with attributes */ + "MXML_INTEGER", /* Integer value */ + "MXML_OPAQUE", /* Opaque string */ + "MXML_REAL", /* Real value */ + "MXML_TEXT", /* Text fragment */ + "MXML_CUSTOM" /* Custom data */ + }; + + + /* + * Read elements and other nodes from the file... + */ + + if ((buffer = malloc(64)) == NULL) + { + mxml_error("Unable to allocate string buffer!"); + return (NULL); + } + + bufsize = 64; + bufptr = buffer; + parent = top; + first = NULL; + whitespace = 0; + encoding = ENCODE_UTF8; + + if (cb && parent) + type = (*cb)(parent); + else + type = MXML_TEXT; + + while ((ch = (*getc_cb)(p, &encoding)) != EOF) + { + if ((ch == '<' || + (mxml_isspace(ch) && type != MXML_OPAQUE && type != MXML_CUSTOM)) && + bufptr > buffer) + { + /* + * Add a new value node... + */ + + *bufptr = '\0'; + + switch (type) + { + case MXML_INTEGER : + node = mxmlNewInteger(parent, strtol(buffer, &bufptr, 0)); + break; + + case MXML_OPAQUE : + node = mxmlNewOpaque(parent, buffer); + break; + + case MXML_REAL : + node = mxmlNewReal(parent, strtod(buffer, &bufptr)); + break; + + case MXML_TEXT : + node = mxmlNewText(parent, whitespace, buffer); + break; + + case MXML_CUSTOM : + if (global->custom_load_cb) + { + /* + * Use the callback to fill in the custom data... + */ + + node = mxmlNewCustom(parent, NULL, NULL); + + if ((*global->custom_load_cb)(node, buffer)) + { + mxml_error("Bad custom value '%s' in parent <%s>!", + buffer, parent ? parent->value.element.name : "null"); + mxmlDelete(node); + node = NULL; + } + break; + } + + default : /* Ignore... */ + node = NULL; + break; + } + + if (*bufptr) + { + /* + * Bad integer/real number value... + */ + + mxml_error("Bad %s value '%s' in parent <%s>!", + type == MXML_INTEGER ? "integer" : "real", buffer, + parent ? parent->value.element.name : "null"); + break; + } + + bufptr = buffer; + whitespace = mxml_isspace(ch) && type == MXML_TEXT; + + if (!node && type != MXML_IGNORE) + { + /* + * Print error and return... + */ + + mxml_error("Unable to add value node of type %s to parent <%s>!", + types[type], parent ? parent->value.element.name : "null"); + goto error; + } + + if (sax_cb) + { + (*sax_cb)(node, MXML_SAX_DATA, sax_data); + + if (!mxmlRelease(node)) + node = NULL; + } + + if (!first && node) + first = node; + } + else if (mxml_isspace(ch) && type == MXML_TEXT) + whitespace = 1; + + /* + * Add lone whitespace node if we have an element and existing + * whitespace... + */ + + if (ch == '<' && whitespace && type == MXML_TEXT) + { + if (parent) + { + node = mxmlNewText(parent, whitespace, ""); + + if (sax_cb) + { + (*sax_cb)(node, MXML_SAX_DATA, sax_data); + + if (!mxmlRelease(node)) + node = NULL; + } + + if (!first && node) + first = node; + } + + whitespace = 0; + } + + if (ch == '<') + { + /* + * Start of open/close tag... + */ + + bufptr = buffer; + + while ((ch = (*getc_cb)(p, &encoding)) != EOF) + if (mxml_isspace(ch) || ch == '>' || (ch == '/' && bufptr > buffer)) + break; + else if (ch == '<') + { + mxml_error("Bare < in element!"); + goto error; + } + else if (ch == '&') + { + if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF) + goto error; + + if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + } + else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + else if (((bufptr - buffer) == 1 && buffer[0] == '?') || + ((bufptr - buffer) == 3 && !strncmp(buffer, "!--", 3)) || + ((bufptr - buffer) == 8 && !strncmp(buffer, "![CDATA[", 8))) + break; + + *bufptr = '\0'; + + if (!strcmp(buffer, "!--")) + { + /* + * Gather rest of comment... + */ + + while ((ch = (*getc_cb)(p, &encoding)) != EOF) + { + if (ch == '>' && bufptr > (buffer + 4) && + bufptr[-3] != '-' && bufptr[-2] == '-' && bufptr[-1] == '-') + break; + else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + } + + /* + * Error out if we didn't get the whole comment... + */ + + if (ch != '>') + { + /* + * Print error and return... + */ + + mxml_error("Early EOF in comment node!"); + goto error; + } + + + /* + * Otherwise add this as an element under the current parent... + */ + + *bufptr = '\0'; + + if (!parent && first) + { + /* + * There can only be one root element! + */ + + mxml_error("<%s> cannot be a second root node after <%s>", + buffer, first->value.element.name); + goto error; + } + + if ((node = mxmlNewElement(parent, buffer)) == NULL) + { + /* + * Just print error for now... + */ + + mxml_error("Unable to add comment node to parent <%s>!", + parent ? parent->value.element.name : "null"); + break; + } + + if (sax_cb) + { + (*sax_cb)(node, MXML_SAX_COMMENT, sax_data); + + if (!mxmlRelease(node)) + node = NULL; + } + + if (node && !first) + first = node; + } + else if (!strcmp(buffer, "![CDATA[")) + { + /* + * Gather CDATA section... + */ + + while ((ch = (*getc_cb)(p, &encoding)) != EOF) + { + if (ch == '>' && !strncmp(bufptr - 2, "]]", 2)) + break; + else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + } + + /* + * Error out if we didn't get the whole comment... + */ + + if (ch != '>') + { + /* + * Print error and return... + */ + + mxml_error("Early EOF in CDATA node!"); + goto error; + } + + + /* + * Otherwise add this as an element under the current parent... + */ + + *bufptr = '\0'; + + if (!parent && first) + { + /* + * There can only be one root element! + */ + + mxml_error("<%s> cannot be a second root node after <%s>", + buffer, first->value.element.name); + goto error; + } + + if ((node = mxmlNewElement(parent, buffer)) == NULL) + { + /* + * Print error and return... + */ + + mxml_error("Unable to add CDATA node to parent <%s>!", + parent ? parent->value.element.name : "null"); + goto error; + } + + if (sax_cb) + { + (*sax_cb)(node, MXML_SAX_CDATA, sax_data); + + if (!mxmlRelease(node)) + node = NULL; + } + + if (node && !first) + first = node; + } + else if (buffer[0] == '?') + { + /* + * Gather rest of processing instruction... + */ + + while ((ch = (*getc_cb)(p, &encoding)) != EOF) + { + if (ch == '>' && bufptr > buffer && bufptr[-1] == '?') + break; + else if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + } + + /* + * Error out if we didn't get the whole processing instruction... + */ + + if (ch != '>') + { + /* + * Print error and return... + */ + + mxml_error("Early EOF in processing instruction node!"); + goto error; + } + + /* + * Otherwise add this as an element under the current parent... + */ + + *bufptr = '\0'; + + if (!parent && first) + { + /* + * There can only be one root element! + */ + + mxml_error("<%s> cannot be a second root node after <%s>", + buffer, first->value.element.name); + goto error; + } + + if ((node = mxmlNewElement(parent, buffer)) == NULL) + { + /* + * Print error and return... + */ + + mxml_error("Unable to add processing instruction node to parent <%s>!", + parent ? parent->value.element.name : "null"); + goto error; + } + + if (sax_cb) + { + (*sax_cb)(node, MXML_SAX_DIRECTIVE, sax_data); + + if (!mxmlRelease(node)) + node = NULL; + } + + if (node) + { + if (!first) + first = node; + + if (!parent) + { + parent = node; + + if (cb) + type = (*cb)(parent); + } + } + } + else if (buffer[0] == '!') + { + /* + * Gather rest of declaration... + */ + + do + { + if (ch == '>') + break; + else + { + if (ch == '&') + if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF) + goto error; + + if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + } + } + while ((ch = (*getc_cb)(p, &encoding)) != EOF); + + /* + * Error out if we didn't get the whole declaration... + */ + + if (ch != '>') + { + /* + * Print error and return... + */ + + mxml_error("Early EOF in declaration node!"); + goto error; + } + + /* + * Otherwise add this as an element under the current parent... + */ + + *bufptr = '\0'; + + if (!parent && first) + { + /* + * There can only be one root element! + */ + + mxml_error("<%s> cannot be a second root node after <%s>", + buffer, first->value.element.name); + goto error; + } + + if ((node = mxmlNewElement(parent, buffer)) == NULL) + { + /* + * Print error and return... + */ + + mxml_error("Unable to add declaration node to parent <%s>!", + parent ? parent->value.element.name : "null"); + goto error; + } + + if (sax_cb) + { + (*sax_cb)(node, MXML_SAX_DIRECTIVE, sax_data); + + if (!mxmlRelease(node)) + node = NULL; + } + + if (node) + { + if (!first) + first = node; + + if (!parent) + { + parent = node; + + if (cb) + type = (*cb)(parent); + } + } + } + else if (buffer[0] == '/') + { + /* + * Handle close tag... + */ + + if (!parent || strcmp(buffer + 1, parent->value.element.name)) + { + /* + * Close tag doesn't match tree; print an error for now... + */ + + mxml_error("Mismatched close tag <%s> under parent <%s>!", + buffer, parent ? parent->value.element.name : "(null)"); + goto error; + } + + /* + * Keep reading until we see >... + */ + + while (ch != '>' && ch != EOF) + ch = (*getc_cb)(p, &encoding); + + node = parent; + parent = parent->parent; + + if (sax_cb) + { + (*sax_cb)(node, MXML_SAX_ELEMENT_CLOSE, sax_data); + + if (!mxmlRelease(node) && first == node) + first = NULL; + } + + /* + * Ascend into the parent and set the value type as needed... + */ + + if (cb && parent) + type = (*cb)(parent); + } + else + { + /* + * Handle open tag... + */ + + if (!parent && first) + { + /* + * There can only be one root element! + */ + + mxml_error("<%s> cannot be a second root node after <%s>", + buffer, first->value.element.name); + goto error; + } + + if ((node = mxmlNewElement(parent, buffer)) == NULL) + { + /* + * Just print error for now... + */ + + mxml_error("Unable to add element node to parent <%s>!", + parent ? parent->value.element.name : "null"); + goto error; + } + + if (mxml_isspace(ch)) + { + if ((ch = mxml_parse_element(node, p, &encoding, getc_cb)) == EOF) + goto error; + } + else if (ch == '/') + { + if ((ch = (*getc_cb)(p, &encoding)) != '>') + { + mxml_error("Expected > but got '%c' instead for element <%s/>!", + ch, buffer); + mxmlDelete(node); + goto error; + } + + ch = '/'; + } + + if (sax_cb) + (*sax_cb)(node, MXML_SAX_ELEMENT_OPEN, sax_data); + + if (!first) + first = node; + + if (ch == EOF) + break; + + if (ch != '/') + { + /* + * Descend into this node, setting the value type as needed... + */ + + parent = node; + + if (cb && parent) + type = (*cb)(parent); + } + else if (sax_cb) + { + (*sax_cb)(node, MXML_SAX_ELEMENT_CLOSE, sax_data); + + if (!mxmlRelease(node) && first == node) + first = NULL; + } + } + + bufptr = buffer; + } + else if (ch == '&') + { + /* + * Add character entity to current buffer... + */ + + if ((ch = mxml_get_entity(parent, p, &encoding, getc_cb)) == EOF) + goto error; + + if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + } + else if (type == MXML_OPAQUE || type == MXML_CUSTOM || !mxml_isspace(ch)) + { + /* + * Add character to current buffer... + */ + + if (mxml_add_char(ch, &bufptr, &buffer, &bufsize)) + goto error; + } + } + + /* + * Free the string buffer - we don't need it anymore... + */ + + free(buffer); + + /* + * Find the top element and return it... + */ + + if (parent) + { + node = parent; + + while (parent->parent != top && parent->parent) + parent = parent->parent; + + if (node != parent) + { + mxml_error("Missing close tag under parent <%s>!", + node->value.element.name, + node->parent ? node->parent->value.element.name : "(null)"); + + mxmlDelete(first); + + return (NULL); + } + } + + if (parent) + return (parent); + else + return (first); + + /* + * Common error return... + */ + +error: + + mxmlDelete(first); + + free(buffer); + + return (NULL); +} + + +/* + * 'mxml_parse_element()' - Parse an element for any attributes... + */ + +static int /* O - Terminating character */ +mxml_parse_element( + mxml_node_t *node, /* I - Element node */ + void *p, /* I - Data to read from */ + int *encoding, /* IO - Encoding */ + _mxml_getc_cb_t getc_cb) /* I - Data callback */ +{ + int ch, /* Current character in file */ + quote; /* Quoting character */ + char *name, /* Attribute name */ + *value, /* Attribute value */ + *ptr; /* Pointer into name/value */ + int namesize, /* Size of name string */ + valsize; /* Size of value string */ + + + /* + * Initialize the name and value buffers... + */ + + if ((name = malloc(64)) == NULL) + { + mxml_error("Unable to allocate memory for name!"); + return (EOF); + } + + namesize = 64; + + if ((value = malloc(64)) == NULL) + { + free(name); + mxml_error("Unable to allocate memory for value!"); + return (EOF); + } + + valsize = 64; + + /* + * Loop until we hit a >, /, ?, or EOF... + */ + + while ((ch = (*getc_cb)(p, encoding)) != EOF) + { +#if DEBUG > 1 + fprintf(stderr, "parse_element: ch='%c'\n", ch); +#endif /* DEBUG > 1 */ + + /* + * Skip leading whitespace... + */ + + if (mxml_isspace(ch)) + continue; + + /* + * Stop at /, ?, or >... + */ + + if (ch == '/' || ch == '?') + { + /* + * Grab the > character and print an error if it isn't there... + */ + + quote = (*getc_cb)(p, encoding); + + if (quote != '>') + { + mxml_error("Expected '>' after '%c' for element %s, but got '%c'!", + ch, node->value.element.name, quote); + goto error; + } + + break; + } + else if (ch == '<') + { + mxml_error("Bare < in element %s!", node->value.element.name); + goto error; + } + else if (ch == '>') + break; + + /* + * Read the attribute name... + */ + + name[0] = ch; + ptr = name + 1; + + if (ch == '\"' || ch == '\'') + { + /* + * Name is in quotes, so get a quoted string... + */ + + quote = ch; + + while ((ch = (*getc_cb)(p, encoding)) != EOF) + { + if (ch == '&') + if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF) + goto error; + + if (mxml_add_char(ch, &ptr, &name, &namesize)) + goto error; + + if (ch == quote) + break; + } + } + else + { + /* + * Grab an normal, non-quoted name... + */ + + while ((ch = (*getc_cb)(p, encoding)) != EOF) + if (mxml_isspace(ch) || ch == '=' || ch == '/' || ch == '>' || + ch == '?') + break; + else + { + if (ch == '&') + if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF) + goto error; + + if (mxml_add_char(ch, &ptr, &name, &namesize)) + goto error; + } + } + + *ptr = '\0'; + + if (mxmlElementGetAttr(node, name)) + goto error; + + while (ch != EOF && mxml_isspace(ch)) + ch = (*getc_cb)(p, encoding); + + if (ch == '=') + { + /* + * Read the attribute value... + */ + + while ((ch = (*getc_cb)(p, encoding)) != EOF && mxml_isspace(ch)); + + if (ch == EOF) + { + mxml_error("Missing value for attribute '%s' in element %s!", + name, node->value.element.name); + goto error; + } + + if (ch == '\'' || ch == '\"') + { + /* + * Read quoted value... + */ + + quote = ch; + ptr = value; + + while ((ch = (*getc_cb)(p, encoding)) != EOF) + if (ch == quote) + break; + else + { + if (ch == '&') + if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF) + goto error; + + if (mxml_add_char(ch, &ptr, &value, &valsize)) + goto error; + } + + *ptr = '\0'; + } + else + { + /* + * Read unquoted value... + */ + + value[0] = ch; + ptr = value + 1; + + while ((ch = (*getc_cb)(p, encoding)) != EOF) + if (mxml_isspace(ch) || ch == '=' || ch == '/' || ch == '>') + break; + else + { + if (ch == '&') + if ((ch = mxml_get_entity(node, p, encoding, getc_cb)) == EOF) + goto error; + + if (mxml_add_char(ch, &ptr, &value, &valsize)) + goto error; + } + + *ptr = '\0'; + } + + /* + * Set the attribute with the given string value... + */ + + mxmlElementSetAttr(node, name, value); + } + else + { + mxml_error("Missing value for attribute '%s' in element %s!", + name, node->value.element.name); + goto error; + } + + /* + * Check the end character... + */ + + if (ch == '/' || ch == '?') + { + /* + * Grab the > character and print an error if it isn't there... + */ + + quote = (*getc_cb)(p, encoding); + + if (quote != '>') + { + mxml_error("Expected '>' after '%c' for element %s, but got '%c'!", + ch, node->value.element.name, quote); + ch = EOF; + } + + break; + } + else if (ch == '>') + break; + } + + /* + * Free the name and value buffers and return... + */ + + free(name); + free(value); + + return (ch); + + /* + * Common error return point... + */ + +error: + + free(name); + free(value); + + return (EOF); +} + + +/* + * 'mxml_string_getc()' - Get a character from a string. + */ + +static int /* O - Character or EOF */ +mxml_string_getc(void *p, /* I - Pointer to file */ + int *encoding) /* IO - Encoding */ +{ + int ch; /* Character */ + const char **s; /* Pointer to string pointer */ + + + s = (const char **)p; + + if ((ch = (*s)[0] & 255) != 0 || *encoding == ENCODE_UTF16LE) + { + /* + * Got character; convert UTF-8 to integer and return... + */ + + (*s)++; + + switch (*encoding) + { + case ENCODE_UTF8 : + if (!(ch & 0x80)) + { +#if DEBUG > 1 + printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } + + return (ch); + } + else if (ch == 0xfe) + { + /* + * UTF-16 big-endian BOM? + */ + + if (((*s)[0] & 255) != 0xff) + return (EOF); + + *encoding = ENCODE_UTF16BE; + (*s)++; + + return (mxml_string_getc(p, encoding)); + } + else if (ch == 0xff) + { + /* + * UTF-16 little-endian BOM? + */ + + if (((*s)[0] & 255) != 0xfe) + return (EOF); + + *encoding = ENCODE_UTF16LE; + (*s)++; + + return (mxml_string_getc(p, encoding)); + } + else if ((ch & 0xe0) == 0xc0) + { + /* + * Two-byte value... + */ + + if (((*s)[0] & 0xc0) != 0x80) + return (EOF); + + ch = ((ch & 0x1f) << 6) | ((*s)[0] & 0x3f); + + (*s)++; + + if (ch < 0x80) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + +#if DEBUG > 1 + printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + return (ch); + } + else if ((ch & 0xf0) == 0xe0) + { + /* + * Three-byte value... + */ + + if (((*s)[0] & 0xc0) != 0x80 || + ((*s)[1] & 0xc0) != 0x80) + return (EOF); + + ch = ((((ch & 0x0f) << 6) | ((*s)[0] & 0x3f)) << 6) | ((*s)[1] & 0x3f); + + (*s) += 2; + + if (ch < 0x800) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + + /* + * Ignore (strip) Byte Order Mark (BOM)... + */ + + if (ch == 0xfeff) + return (mxml_string_getc(p, encoding)); + +#if DEBUG > 1 + printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + return (ch); + } + else if ((ch & 0xf8) == 0xf0) + { + /* + * Four-byte value... + */ + + if (((*s)[0] & 0xc0) != 0x80 || + ((*s)[1] & 0xc0) != 0x80 || + ((*s)[2] & 0xc0) != 0x80) + return (EOF); + + ch = ((((((ch & 0x07) << 6) | ((*s)[0] & 0x3f)) << 6) | + ((*s)[1] & 0x3f)) << 6) | ((*s)[2] & 0x3f); + + (*s) += 3; + + if (ch < 0x10000) + { + mxml_error("Invalid UTF-8 sequence for character 0x%04x!", ch); + return (EOF); + } + +#if DEBUG > 1 + printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + return (ch); + } + else + return (EOF); + + case ENCODE_UTF16BE : + /* + * Read UTF-16 big-endian char... + */ + + ch = (ch << 8) | ((*s)[0] & 255); + (*s) ++; + + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } + else if (ch >= 0xd800 && ch <= 0xdbff) + { + /* + * Multi-word UTF-16 char... + */ + + int lch; /* Lower word */ + + + if (!(*s)[0]) + return (EOF); + + lch = (((*s)[0] & 255) << 8) | ((*s)[1] & 255); + (*s) += 2; + + if (lch < 0xdc00 || lch >= 0xdfff) + return (EOF); + + ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; + } + +#if DEBUG > 1 + printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + return (ch); + + case ENCODE_UTF16LE : + /* + * Read UTF-16 little-endian char... + */ + + ch = ch | (((*s)[0] & 255) << 8); + + if (!ch) + { + (*s) --; + return (EOF); + } + + (*s) ++; + + if (mxml_bad_char(ch)) + { + mxml_error("Bad control character 0x%02x not allowed by XML standard!", + ch); + return (EOF); + } + else if (ch >= 0xd800 && ch <= 0xdbff) + { + /* + * Multi-word UTF-16 char... + */ + + int lch; /* Lower word */ + + + if (!(*s)[1]) + return (EOF); + + lch = (((*s)[1] & 255) << 8) | ((*s)[0] & 255); + (*s) += 2; + + if (lch < 0xdc00 || lch >= 0xdfff) + return (EOF); + + ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000; + } + +#if DEBUG > 1 + printf("mxml_string_getc: %c (0x%04x)\n", ch < ' ' ? '.' : ch, ch); +#endif /* DEBUG > 1 */ + + return (ch); + } + } + + return (EOF); +} + + +/* + * 'mxml_string_putc()' - Write a character to a string. + */ + +static int /* O - 0 on success, -1 on failure */ +mxml_string_putc(int ch, /* I - Character to write */ + void *p) /* I - Pointer to string pointers */ +{ + char **pp; /* Pointer to string pointers */ + + + pp = (char **)p; + + if (pp[0] < pp[1]) + pp[0][0] = ch; + + pp[0] ++; + + return (0); +} + + +/* + * 'mxml_write_name()' - Write a name string. + */ + +static int /* O - 0 on success, -1 on failure */ +mxml_write_name(const char *s, /* I - Name to write */ + void *p, /* I - Write pointer */ + int (*putc_cb)(int, void *)) + /* I - Write callback */ +{ + char quote; /* Quote character */ + const char *name; /* Entity name */ + + + if (*s == '\"' || *s == '\'') + { + /* + * Write a quoted name string... + */ + + if ((*putc_cb)(*s, p) < 0) + return (-1); + + quote = *s++; + + while (*s && *s != quote) + { + if ((name = mxmlEntityGetName(*s)) != NULL) + { + if ((*putc_cb)('&', p) < 0) + return (-1); + + while (*name) + { + if ((*putc_cb)(*name, p) < 0) + return (-1); + + name ++; + } + + if ((*putc_cb)(';', p) < 0) + return (-1); + } + else if ((*putc_cb)(*s, p) < 0) + return (-1); + + s ++; + } + + /* + * Write the end quote... + */ + + if ((*putc_cb)(quote, p) < 0) + return (-1); + } + else + { + /* + * Write a non-quoted name string... + */ + + while (*s) + { + if ((*putc_cb)(*s, p) < 0) + return (-1); + + s ++; + } + } + + return (0); +} + + +/* + * 'mxml_write_node()' - Save an XML node to a file. + */ + +static int /* O - Column or -1 on error */ +mxml_write_node(mxml_node_t *node, /* I - Node to write */ + void *p, /* I - File to write to */ + mxml_save_cb_t cb, /* I - Whitespace callback */ + int col, /* I - Current column */ + _mxml_putc_cb_t putc_cb,/* I - Output callback */ + _mxml_global_t *global)/* I - Global data */ +{ + int i, /* Looping var */ + width; /* Width of attr + value */ + mxml_attr_t *attr; /* Current attribute */ + char s[255]; /* Temporary string */ + + + /* + * Print the node value... + */ + + switch (node->type) + { + case MXML_ELEMENT : + col = mxml_write_ws(node, p, cb, MXML_WS_BEFORE_OPEN, col, putc_cb); + + if ((*putc_cb)('<', p) < 0) + return (-1); + if (node->value.element.name[0] == '?' || + !strncmp(node->value.element.name, "!--", 3) || + !strncmp(node->value.element.name, "![CDATA[", 8)) + { + /* + * Comments, CDATA, and processing instructions do not + * use character entities. + */ + + const char *ptr; /* Pointer into name */ + + + for (ptr = node->value.element.name; *ptr; ptr ++) + if ((*putc_cb)(*ptr, p) < 0) + return (-1); + } + else if (mxml_write_name(node->value.element.name, p, putc_cb) < 0) + return (-1); + + col += strlen(node->value.element.name) + 1; + + for (i = node->value.element.num_attrs, attr = node->value.element.attrs; + i > 0; + i --, attr ++) + { + width = strlen(attr->name); + + if (attr->value) + width += strlen(attr->value) + 3; + + if (global->wrap > 0 && (col + width) > global->wrap) + { + if ((*putc_cb)('\n', p) < 0) + return (-1); + + col = 0; + } + else + { + if ((*putc_cb)(' ', p) < 0) + return (-1); + + col ++; + } + + if (mxml_write_name(attr->name, p, putc_cb) < 0) + return (-1); + + if (attr->value) + { + if ((*putc_cb)('=', p) < 0) + return (-1); + if ((*putc_cb)('\"', p) < 0) + return (-1); + if (mxml_write_string(attr->value, p, putc_cb) < 0) + return (-1); + if ((*putc_cb)('\"', p) < 0) + return (-1); + } + + col += width; + } + + if (node->child) + { + /* + * Write children... + */ + + mxml_node_t *child; /* Current child */ + + + if ((*putc_cb)('>', p) < 0) + return (-1); + else + col ++; + + col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb); + + for (child = node->child; child; child = child->next) + { + if ((col = mxml_write_node(child, p, cb, col, putc_cb, global)) < 0) + return (-1); + } + + /* + * The ? and ! elements are special-cases and have no end tags... + */ + + if (node->value.element.name[0] != '!' && + node->value.element.name[0] != '?') + { + col = mxml_write_ws(node, p, cb, MXML_WS_BEFORE_CLOSE, col, putc_cb); + + if ((*putc_cb)('<', p) < 0) + return (-1); + if ((*putc_cb)('/', p) < 0) + return (-1); + if (mxml_write_string(node->value.element.name, p, putc_cb) < 0) + return (-1); + if ((*putc_cb)('>', p) < 0) + return (-1); + + col += strlen(node->value.element.name) + 3; + + col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_CLOSE, col, putc_cb); + } + } + else if (node->value.element.name[0] == '!' || + node->value.element.name[0] == '?') + { + /* + * The ? and ! elements are special-cases... + */ + + if ((*putc_cb)('>', p) < 0) + return (-1); + else + col ++; + + col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb); + } + else + { + if ((*putc_cb)(' ', p) < 0) + return (-1); + if ((*putc_cb)('/', p) < 0) + return (-1); + if ((*putc_cb)('>', p) < 0) + return (-1); + + col += 3; + + col = mxml_write_ws(node, p, cb, MXML_WS_AFTER_OPEN, col, putc_cb); + } + break; + + case MXML_INTEGER : + if (node->prev) + { + if (global->wrap > 0 && col > global->wrap) + { + if ((*putc_cb)('\n', p) < 0) + return (-1); + + col = 0; + } + else if ((*putc_cb)(' ', p) < 0) + return (-1); + else + col ++; + } + + sprintf(s, "%d", node->value.integer); + if (mxml_write_string(s, p, putc_cb) < 0) + return (-1); + + col += strlen(s); + break; + + case MXML_OPAQUE : + if (mxml_write_string(node->value.opaque, p, putc_cb) < 0) + return (-1); + + col += strlen(node->value.opaque); + break; + + case MXML_REAL : + if (node->prev) + { + if (global->wrap > 0 && col > global->wrap) + { + if ((*putc_cb)('\n', p) < 0) + return (-1); + + col = 0; + } + else if ((*putc_cb)(' ', p) < 0) + return (-1); + else + col ++; + } + + sprintf(s, "%f", node->value.real); + if (mxml_write_string(s, p, putc_cb) < 0) + return (-1); + + col += strlen(s); + break; + + case MXML_TEXT : + if (node->value.text.whitespace && col > 0) + { + if (global->wrap > 0 && col > global->wrap) + { + if ((*putc_cb)('\n', p) < 0) + return (-1); + + col = 0; + } + else if ((*putc_cb)(' ', p) < 0) + return (-1); + else + col ++; + } + + if (mxml_write_string(node->value.text.string, p, putc_cb) < 0) + return (-1); + + col += strlen(node->value.text.string); + break; + + case MXML_CUSTOM : + if (global->custom_save_cb) + { + char *data; /* Custom data string */ + const char *newline; /* Last newline in string */ + + + if ((data = (*global->custom_save_cb)(node)) == NULL) + return (-1); + + if (mxml_write_string(data, p, putc_cb) < 0) + return (-1); + + if ((newline = strrchr(data, '\n')) == NULL) + col += strlen(data); + else + col = strlen(newline); + + free(data); + break; + } + + default : /* Should never happen */ + return (-1); + } + + return (col); +} + + +/* + * 'mxml_write_string()' - Write a string, escaping & and < as needed. + */ + +static int /* O - 0 on success, -1 on failure */ +mxml_write_string( + const char *s, /* I - String to write */ + void *p, /* I - Write pointer */ + _mxml_putc_cb_t putc_cb) /* I - Write callback */ +{ + const char *name; /* Entity name, if any */ + + + while (*s) + { + if ((name = mxmlEntityGetName(*s)) != NULL) + { + if ((*putc_cb)('&', p) < 0) + return (-1); + + while (*name) + { + if ((*putc_cb)(*name, p) < 0) + return (-1); + name ++; + } + + if ((*putc_cb)(';', p) < 0) + return (-1); + } + else if ((*putc_cb)(*s, p) < 0) + return (-1); + + s ++; + } + + return (0); +} + + +/* + * 'mxml_write_ws()' - Do whitespace callback... + */ + +static int /* O - New column */ +mxml_write_ws(mxml_node_t *node, /* I - Current node */ + void *p, /* I - Write pointer */ + mxml_save_cb_t cb, /* I - Callback function */ + int ws, /* I - Where value */ + int col, /* I - Current column */ + _mxml_putc_cb_t putc_cb) /* I - Write callback */ +{ + const char *s; /* Whitespace string */ + + + if (cb && (s = (*cb)(node, ws)) != NULL) + { + while (*s) + { + if ((*putc_cb)(*s, p) < 0) + return (-1); + else if (*s == '\n') + col = 0; + else if (*s == '\t') + { + col += MXML_TAB; + col = col - (col % MXML_TAB); + } + else + col ++; + + s ++; + } + } + + return (col); +} + + +/* + * End of "$Id: mxml-file.c 438 2011-03-24 05:47:51Z mike $". + */ diff --git a/daemon/mxml/mxml-get.c b/daemon/mxml/mxml-get.c new file mode 100644 index 0000000..a5356d5 --- /dev/null +++ b/daemon/mxml/mxml-get.c @@ -0,0 +1,471 @@ +/* + * "$Id: mxml-get.c 427 2011-01-03 02:03:29Z mike $" + * + * Node get functions for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2011 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + * mxmlGetCDATA() - Get the value for a CDATA node. + * mxmlGetCustom() - Get the value for a custom node. + * mxmlGetElement() - Get the name for an element node. + * mxmlGetFirstChild() - Get the first child of an element node. + * mxmlGetInteger() - Get the integer value from the specified node or its + * first child. + * mxmlGetLastChild() - Get the last child of an element node. + * mxmlGetNextSibling() - Get the next node for the current parent. + * mxmlGetOpaque() - Get an opaque string value for a node or its first + * child. + * mxmlGetParent() - Get the parent node. + * mxmlGetPrevSibling() - Get the previous node for the current parent. + * mxmlGetReal() - Get the real value for a node or its first child. + * mxmlGetText() - Get the text value for a node or its first child. + * mxmlGetType() - Get the node type. + * mxmlGetUserData() - Get the user data pointer for a node. + */ + +/* + * Include necessary headers... + */ + +#include "config.h" +#include "mxml.h" + + +/* + * 'mxmlGetCDATA()' - Get the value for a CDATA node. + * + * @code NULL@ is returned if the node is not a CDATA element. + * + * @since Mini-XML 2.7@ + */ + +const char * /* O - CDATA value or NULL */ +mxmlGetCDATA(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node || node->type != MXML_ELEMENT || + strncmp(node->value.element.name, "![CDATA[", 8)) + return (NULL); + + /* + * Return the text following the CDATA declaration... + */ + + return (node->value.element.name + 8); +} + + +/* + * 'mxmlGetCustom()' - Get the value for a custom node. + * + * @code NULL@ is returned if the node (or its first child) is not a custom + * value node. + * + * @since Mini-XML 2.7@ + */ + +const void * /* O - Custom value or NULL */ +mxmlGetCustom(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node) + return (NULL); + + /* + * Return the integer value... + */ + + if (node->type == MXML_CUSTOM) + return (node->value.custom.data); + else if (node->type == MXML_ELEMENT && + node->child && + node->child->type == MXML_CUSTOM) + return (node->child->value.custom.data); + else + return (NULL); +} + + +/* + * 'mxmlGetElement()' - Get the name for an element node. + * + * @code NULL@ is returned if the node is not an element node. + * + * @since Mini-XML 2.7@ + */ + +const char * /* O - Element name or NULL */ +mxmlGetElement(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node || node->type != MXML_ELEMENT) + return (NULL); + + /* + * Return the element name... + */ + + return (node->value.element.name); +} + + +/* + * 'mxmlGetFirstChild()' - Get the first child of an element node. + * + * @code NULL@ is returned if the node is not an element node or if the node + * has no children. + * + * @since Mini-XML 2.7@ + */ + +mxml_node_t * /* O - First child or NULL */ +mxmlGetFirstChild(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node || node->type != MXML_ELEMENT) + return (NULL); + + /* + * Return the first child node... + */ + + return (node->child); +} + + +/* + * 'mxmlGetInteger()' - Get the integer value from the specified node or its + * first child. + * + * 0 is returned if the node (or its first child) is not an integer value node. + * + * @since Mini-XML 2.7@ + */ + +int /* O - Integer value or 0 */ +mxmlGetInteger(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node) + return (0); + + /* + * Return the integer value... + */ + + if (node->type == MXML_INTEGER) + return (node->value.integer); + else if (node->type == MXML_ELEMENT && + node->child && + node->child->type == MXML_INTEGER) + return (node->child->value.integer); + else + return (0); +} + + +/* + * 'mxmlGetLastChild()' - Get the last child of an element node. + * + * @code NULL@ is returned if the node is not an element node or if the node + * has no children. + * + * @since Mini-XML 2.7@ + */ + +mxml_node_t * /* O - Last child or NULL */ +mxmlGetLastChild(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node || node->type != MXML_ELEMENT) + return (NULL); + + /* + * Return the node type... + */ + + return (node->last_child); +} + + +/* + * 'mxmlGetNextSibling()' - Get the next node for the current parent. + * + * @code NULL@ is returned if this is the last child for the current parent. + * + * @since Mini-XML 2.7@ + */ + +mxml_node_t * +mxmlGetNextSibling(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node) + return (NULL); + + /* + * Return the node type... + */ + + return (node->next); +} + + +/* + * 'mxmlGetOpaque()' - Get an opaque string value for a node or its first child. + * + * @code NULL@ is returned if the node (or its first child) is not an opaque + * value node. + * + * @since Mini-XML 2.7@ + */ + +const char * /* O - Opaque string or NULL */ +mxmlGetOpaque(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node) + return (NULL); + + /* + * Return the integer value... + */ + + if (node->type == MXML_OPAQUE) + return (node->value.opaque); + else if (node->type == MXML_ELEMENT && + node->child && + node->child->type == MXML_OPAQUE) + return (node->child->value.opaque); + else + return (NULL); +} + + +/* + * 'mxmlGetParent()' - Get the parent node. + * + * @code NULL@ is returned for a root node. + * + * @since Mini-XML 2.7@ + */ + +mxml_node_t * /* O - Parent node or NULL */ +mxmlGetParent(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node) + return (NULL); + + /* + * Return the node type... + */ + + return (node->parent); +} + + +/* + * 'mxmlGetPrevSibling()' - Get the previous node for the current parent. + * + * @code NULL@ is returned if this is the first child for the current parent. + * + * @since Mini-XML 2.7@ + */ + +mxml_node_t * /* O - Previous node or NULL */ +mxmlGetPrevSibling(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node) + return (NULL); + + /* + * Return the node type... + */ + + return (node->prev); +} + + +/* + * 'mxmlGetReal()' - Get the real value for a node or its first child. + * + * 0.0 is returned if the node (or its first child) is not a real value node. + * + * @since Mini-XML 2.7@ + */ + +double /* O - Real value or 0.0 */ +mxmlGetReal(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node) + return (0.0); + + /* + * Return the integer value... + */ + + if (node->type == MXML_REAL) + return (node->value.real); + else if (node->type == MXML_ELEMENT && + node->child && + node->child->type == MXML_REAL) + return (node->child->value.real); + else + return (0.0); +} + + +/* + * 'mxmlGetText()' - Get the text value for a node or its first child. + * + * @code NULL@ is returned if the node (or its first child) is not a text node. + * The "whitespace" argument can be NULL. + * + * @since Mini-XML 2.7@ + */ + +const char * /* O - Text string or NULL */ +mxmlGetText(mxml_node_t *node, /* I - Node to get */ + int *whitespace) /* O - 1 if string is preceded by whitespace, 0 otherwise */ +{ + /* + * Range check input... + */ + + if (!node) + { + if (whitespace) + *whitespace = 0; + + return (NULL); + } + + /* + * Return the integer value... + */ + + if (node->type == MXML_TEXT) + { + if (whitespace) + *whitespace = node->value.text.whitespace; + + return (node->value.text.string); + } + else if (node->type == MXML_ELEMENT && + node->child && + node->child->type == MXML_TEXT) + { + if (whitespace) + *whitespace = node->child->value.text.whitespace; + + return (node->child->value.text.string); + } + else + { + if (whitespace) + *whitespace = 0; + + return (NULL); + } +} + + +/* + * 'mxmlGetType()' - Get the node type. + * + * @code MXML_IGNORE@ is returned if "node" is @code NULL@. + * + * @since Mini-XML 2.7@ + */ + +mxml_type_t /* O - Type of node */ +mxmlGetType(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node) + return (MXML_IGNORE); + + /* + * Return the node type... + */ + + return (node->type); +} + + +/* + * 'mxmlGetUserData()' - Get the user data pointer for a node. + * + * @since Mini-XML 2.7@ + */ + +void * /* O - User data pointer */ +mxmlGetUserData(mxml_node_t *node) /* I - Node to get */ +{ + /* + * Range check input... + */ + + if (!node) + return (NULL); + + /* + * Return the user data pointer... + */ + + return (node->user_data); +} + + +/* + * End of "$Id: mxml-get.c 427 2011-01-03 02:03:29Z mike $". + */ diff --git a/daemon/mxml/mxml-index.c b/daemon/mxml/mxml-index.c new file mode 100644 index 0000000..b6efc66 --- /dev/null +++ b/daemon/mxml/mxml-index.c @@ -0,0 +1,662 @@ +/* + * "$Id: mxml-index.c 426 2011-01-01 23:42:17Z mike $" + * + * Index support code for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2011 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + */ + +/* + * Include necessary headers... + */ + +#include "config.h" +#include "mxml.h" + + +/* + * Sort functions... + */ + +static int index_compare(mxml_index_t *ind, mxml_node_t *first, + mxml_node_t *second); +static int index_find(mxml_index_t *ind, const char *element, + const char *value, mxml_node_t *node); +static void index_sort(mxml_index_t *ind, int left, int right); + + +/* + * 'mxmlIndexDelete()' - Delete an index. + */ + +void +mxmlIndexDelete(mxml_index_t *ind) /* I - Index to delete */ +{ + /* + * Range check input.. + */ + + if (!ind) + return; + + /* + * Free memory... + */ + + if (ind->attr) + free(ind->attr); + + if (ind->alloc_nodes) + free(ind->nodes); + + free(ind); +} + + +/* + * 'mxmlIndexEnum()' - Return the next node in the index. + * + * Nodes are returned in the sorted order of the index. + */ + +mxml_node_t * /* O - Next node or NULL if there is none */ +mxmlIndexEnum(mxml_index_t *ind) /* I - Index to enumerate */ +{ + /* + * Range check input... + */ + + if (!ind) + return (NULL); + + /* + * Return the next node... + */ + + if (ind->cur_node < ind->num_nodes) + return (ind->nodes[ind->cur_node ++]); + else + return (NULL); +} + + +/* + * 'mxmlIndexFind()' - Find the next matching node. + * + * You should call mxmlIndexReset() prior to using this function for + * the first time with a particular set of "element" and "value" + * strings. Passing NULL for both "element" and "value" is equivalent + * to calling mxmlIndexEnum(). + */ + +mxml_node_t * /* O - Node or NULL if none found */ +mxmlIndexFind(mxml_index_t *ind, /* I - Index to search */ + const char *element, /* I - Element name to find, if any */ + const char *value) /* I - Attribute value, if any */ +{ + int diff, /* Difference between names */ + current, /* Current entity in search */ + first, /* First entity in search */ + last; /* Last entity in search */ + + +#ifdef DEBUG + printf("mxmlIndexFind(ind=%p, element=\"%s\", value=\"%s\")\n", + ind, element ? element : "(null)", value ? value : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!ind || (!ind->attr && value)) + { +#ifdef DEBUG + puts(" returning NULL..."); + printf(" ind->attr=\"%s\"\n", ind->attr ? ind->attr : "(null)"); +#endif /* DEBUG */ + + return (NULL); + } + + /* + * If both element and value are NULL, just enumerate the nodes in the + * index... + */ + + if (!element && !value) + return (mxmlIndexEnum(ind)); + + /* + * If there are no nodes in the index, return NULL... + */ + + if (!ind->num_nodes) + { +#ifdef DEBUG + puts(" returning NULL..."); + puts(" no nodes!"); +#endif /* DEBUG */ + + return (NULL); + } + + /* + * If cur_node == 0, then find the first matching node... + */ + + if (ind->cur_node == 0) + { + /* + * Find the first node using a modified binary search algorithm... + */ + + first = 0; + last = ind->num_nodes - 1; + +#ifdef DEBUG + printf(" find first time, num_nodes=%d...\n", ind->num_nodes); +#endif /* DEBUG */ + + while ((last - first) > 1) + { + current = (first + last) / 2; + +#ifdef DEBUG + printf(" first=%d, last=%d, current=%d\n", first, last, current); +#endif /* DEBUG */ + + if ((diff = index_find(ind, element, value, ind->nodes[current])) == 0) + { + /* + * Found a match, move back to find the first... + */ + +#ifdef DEBUG + puts(" match!"); +#endif /* DEBUG */ + + while (current > 0 && + !index_find(ind, element, value, ind->nodes[current - 1])) + current --; + +#ifdef DEBUG + printf(" returning first match=%d\n", current); +#endif /* DEBUG */ + + /* + * Return the first match and save the index to the next... + */ + + ind->cur_node = current + 1; + + return (ind->nodes[current]); + } + else if (diff < 0) + last = current; + else + first = current; + +#ifdef DEBUG + printf(" diff=%d\n", diff); +#endif /* DEBUG */ + } + + /* + * If we get this far, then we found exactly 0 or 1 matches... + */ + + for (current = first; current <= last; current ++) + if (!index_find(ind, element, value, ind->nodes[current])) + { + /* + * Found exactly one (or possibly two) match... + */ + +#ifdef DEBUG + printf(" returning only match %d...\n", current); +#endif /* DEBUG */ + + ind->cur_node = current + 1; + + return (ind->nodes[current]); + } + + /* + * No matches... + */ + + ind->cur_node = ind->num_nodes; + +#ifdef DEBUG + puts(" returning NULL..."); +#endif /* DEBUG */ + + return (NULL); + } + else if (ind->cur_node < ind->num_nodes && + !index_find(ind, element, value, ind->nodes[ind->cur_node])) + { + /* + * Return the next matching node... + */ + +#ifdef DEBUG + printf(" returning next match %d...\n", ind->cur_node); +#endif /* DEBUG */ + + return (ind->nodes[ind->cur_node ++]); + } + + /* + * If we get this far, then we have no matches... + */ + + ind->cur_node = ind->num_nodes; + +#ifdef DEBUG + puts(" returning NULL..."); +#endif /* DEBUG */ + + return (NULL); +} + + +/* + * 'mxmlIndexGetCount()' - Get the number of nodes in an index. + * + * @since Mini-XML 2.7@ + */ + +int /* I - Number of nodes in index */ +mxmlIndexGetCount(mxml_index_t *ind) /* I - Index of nodes */ +{ + /* + * Range check input... + */ + + if (!ind) + return (0); + + /* + * Return the number of nodes in the index... + */ + + return (ind->num_nodes); +} + + +/* + * 'mxmlIndexNew()' - Create a new index. + * + * The index will contain all nodes that contain the named element and/or + * attribute. If both "element" and "attr" are NULL, then the index will + * contain a sorted list of the elements in the node tree. Nodes are + * sorted by element name and optionally by attribute value if the "attr" + * argument is not NULL. + */ + +mxml_index_t * /* O - New index */ +mxmlIndexNew(mxml_node_t *node, /* I - XML node tree */ + const char *element, /* I - Element to index or NULL for all */ + const char *attr) /* I - Attribute to index or NULL for none */ +{ + mxml_index_t *ind; /* New index */ + mxml_node_t *current, /* Current node in index */ + **temp; /* Temporary node pointer array */ + + + /* + * Range check input... + */ + +#ifdef DEBUG + printf("mxmlIndexNew(node=%p, element=\"%s\", attr=\"%s\")\n", + node, element ? element : "(null)", attr ? attr : "(null)"); +#endif /* DEBUG */ + + if (!node) + return (NULL); + + /* + * Create a new index... + */ + + if ((ind = calloc(1, sizeof(mxml_index_t))) == NULL) + { + mxml_error("Unable to allocate %d bytes for index - %s", + sizeof(mxml_index_t), strerror(errno)); + return (NULL); + } + + if (attr) + ind->attr = strdup(attr); + + if (!element && !attr) + current = node; + else + current = mxmlFindElement(node, node, element, attr, NULL, MXML_DESCEND); + + while (current) + { + if (ind->num_nodes >= ind->alloc_nodes) + { + if (!ind->alloc_nodes) + temp = malloc(64 * sizeof(mxml_node_t *)); + else + temp = realloc(ind->nodes, (ind->alloc_nodes + 64) * sizeof(mxml_node_t *)); + + if (!temp) + { + /* + * Unable to allocate memory for the index, so abort... + */ + + mxml_error("Unable to allocate %d bytes for index: %s", + (ind->alloc_nodes + 64) * sizeof(mxml_node_t *), + strerror(errno)); + + mxmlIndexDelete(ind); + return (NULL); + } + + ind->nodes = temp; + ind->alloc_nodes += 64; + } + + ind->nodes[ind->num_nodes ++] = current; + + current = mxmlFindElement(current, node, element, attr, NULL, MXML_DESCEND); + } + + /* + * Sort nodes based upon the search criteria... + */ + +#ifdef DEBUG + { + int i; /* Looping var */ + + + printf("%d node(s) in index.\n\n", ind->num_nodes); + + if (attr) + { + printf("Node Address Element %s\n", attr); + puts("-------- -------- -------------- ------------------------------"); + + for (i = 0; i < ind->num_nodes; i ++) + printf("%8d %-8p %-14.14s %s\n", i, ind->nodes[i], + ind->nodes[i]->value.element.name, + mxmlElementGetAttr(ind->nodes[i], attr)); + } + else + { + puts("Node Address Element"); + puts("-------- -------- --------------"); + + for (i = 0; i < ind->num_nodes; i ++) + printf("%8d %-8p %s\n", i, ind->nodes[i], + ind->nodes[i]->value.element.name); + } + + putchar('\n'); + } +#endif /* DEBUG */ + + if (ind->num_nodes > 1) + index_sort(ind, 0, ind->num_nodes - 1); + +#ifdef DEBUG + { + int i; /* Looping var */ + + + puts("After sorting:\n"); + + if (attr) + { + printf("Node Address Element %s\n", attr); + puts("-------- -------- -------------- ------------------------------"); + + for (i = 0; i < ind->num_nodes; i ++) + printf("%8d %-8p %-14.14s %s\n", i, ind->nodes[i], + ind->nodes[i]->value.element.name, + mxmlElementGetAttr(ind->nodes[i], attr)); + } + else + { + puts("Node Address Element"); + puts("-------- -------- --------------"); + + for (i = 0; i < ind->num_nodes; i ++) + printf("%8d %-8p %s\n", i, ind->nodes[i], + ind->nodes[i]->value.element.name); + } + + putchar('\n'); + } +#endif /* DEBUG */ + + /* + * Return the new index... + */ + + return (ind); +} + + +/* + * 'mxmlIndexReset()' - Reset the enumeration/find pointer in the index and + * return the first node in the index. + * + * This function should be called prior to using mxmlIndexEnum() or + * mxmlIndexFind() for the first time. + */ + +mxml_node_t * /* O - First node or NULL if there is none */ +mxmlIndexReset(mxml_index_t *ind) /* I - Index to reset */ +{ +#ifdef DEBUG + printf("mxmlIndexReset(ind=%p)\n", ind); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!ind) + return (NULL); + + /* + * Set the index to the first element... + */ + + ind->cur_node = 0; + + /* + * Return the first node... + */ + + if (ind->num_nodes) + return (ind->nodes[0]); + else + return (NULL); +} + + +/* + * 'index_compare()' - Compare two nodes. + */ + +static int /* O - Result of comparison */ +index_compare(mxml_index_t *ind, /* I - Index */ + mxml_node_t *first, /* I - First node */ + mxml_node_t *second) /* I - Second node */ +{ + int diff; /* Difference */ + + + /* + * Check the element name... + */ + + if ((diff = strcmp(first->value.element.name, + second->value.element.name)) != 0) + return (diff); + + /* + * Check the attribute value... + */ + + if (ind->attr) + { + if ((diff = strcmp(mxmlElementGetAttr(first, ind->attr), + mxmlElementGetAttr(second, ind->attr))) != 0) + return (diff); + } + + /* + * No difference, return 0... + */ + + return (0); +} + + +/* + * 'index_find()' - Compare a node with index values. + */ + +static int /* O - Result of comparison */ +index_find(mxml_index_t *ind, /* I - Index */ + const char *element, /* I - Element name or NULL */ + const char *value, /* I - Attribute value or NULL */ + mxml_node_t *node) /* I - Node */ +{ + int diff; /* Difference */ + + + /* + * Check the element name... + */ + + if (element) + { + if ((diff = strcmp(element, node->value.element.name)) != 0) + return (diff); + } + + /* + * Check the attribute value... + */ + + if (value) + { + if ((diff = strcmp(value, mxmlElementGetAttr(node, ind->attr))) != 0) + return (diff); + } + + /* + * No difference, return 0... + */ + + return (0); +} + + +/* + * 'index_sort()' - Sort the nodes in the index... + * + * This function implements the classic quicksort algorithm... + */ + +static void +index_sort(mxml_index_t *ind, /* I - Index to sort */ + int left, /* I - Left node in partition */ + int right) /* I - Right node in partition */ +{ + mxml_node_t *pivot, /* Pivot node */ + *temp; /* Swap node */ + int templ, /* Temporary left node */ + tempr; /* Temporary right node */ + + + /* + * Loop until we have sorted all the way to the right... + */ + + do + { + /* + * Sort the pivot in the current partition... + */ + + pivot = ind->nodes[left]; + + for (templ = left, tempr = right; templ < tempr;) + { + /* + * Move left while left node <= pivot node... + */ + + while ((templ < right) && + index_compare(ind, ind->nodes[templ], pivot) <= 0) + templ ++; + + /* + * Move right while right node > pivot node... + */ + + while ((tempr > left) && + index_compare(ind, ind->nodes[tempr], pivot) > 0) + tempr --; + + /* + * Swap nodes if needed... + */ + + if (templ < tempr) + { + temp = ind->nodes[templ]; + ind->nodes[templ] = ind->nodes[tempr]; + ind->nodes[tempr] = temp; + } + } + + /* + * When we get here, the right (tempr) node is the new position for the + * pivot node... + */ + + if (index_compare(ind, pivot, ind->nodes[tempr]) > 0) + { + ind->nodes[left] = ind->nodes[tempr]; + ind->nodes[tempr] = pivot; + } + + /* + * Recursively sort the left partition as needed... + */ + + if (left < (tempr - 1)) + index_sort(ind, left, tempr - 1); + } + while (right > (left = tempr + 1)); +} + + +/* + * End of "$Id: mxml-index.c 426 2011-01-01 23:42:17Z mike $". + */ diff --git a/daemon/mxml/mxml-node.c b/daemon/mxml/mxml-node.c new file mode 100644 index 0000000..44af759 --- /dev/null +++ b/daemon/mxml/mxml-node.c @@ -0,0 +1,807 @@ +/* + * "$Id: mxml-node.c 436 2011-01-22 01:02:05Z mike $" + * + * Node support code for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2011 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + * mxmlAdd() - Add a node to a tree. + * mxmlDelete() - Delete a node and all of its children. + * mxmlGetRefCount() - Get the current reference (use) count for a node. + * mxmlNewCDATA() - Create a new CDATA node. + * mxmlNewCustom() - Create a new custom data node. + * mxmlNewElement() - Create a new element node. + * mxmlNewInteger() - Create a new integer node. + * mxmlNewOpaque() - Create a new opaque string. + * mxmlNewReal() - Create a new real number node. + * mxmlNewText() - Create a new text fragment node. + * mxmlNewTextf() - Create a new formatted text fragment node. + * mxmlRemove() - Remove a node from its parent. + * mxmlNewXML() - Create a new XML document tree. + * mxmlRelease() - Release a node. + * mxmlRetain() - Retain a node. + * mxml_new() - Create a new node. + */ + +/* + * Include necessary headers... + */ + +#include "config.h" +#include "mxml.h" + + +/* + * Local functions... + */ + +static mxml_node_t *mxml_new(mxml_node_t *parent, mxml_type_t type); + + +/* + * 'mxmlAdd()' - Add a node to a tree. + * + * Adds the specified node to the parent. If the child argument is not + * NULL, puts the new node before or after the specified child depending + * on the value of the where argument. If the child argument is NULL, + * puts the new node at the beginning of the child list (MXML_ADD_BEFORE) + * or at the end of the child list (MXML_ADD_AFTER). The constant + * MXML_ADD_TO_PARENT can be used to specify a NULL child pointer. + */ + +void +mxmlAdd(mxml_node_t *parent, /* I - Parent node */ + int where, /* I - Where to add, MXML_ADD_BEFORE or MXML_ADD_AFTER */ + mxml_node_t *child, /* I - Child node for where or MXML_ADD_TO_PARENT */ + mxml_node_t *node) /* I - Node to add */ +{ +#ifdef DEBUG + fprintf(stderr, "mxmlAdd(parent=%p, where=%d, child=%p, node=%p)\n", parent, + where, child, node); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!parent || !node) + return; + +#if DEBUG > 1 + fprintf(stderr, " BEFORE: node->parent=%p\n", node->parent); + if (parent) + { + fprintf(stderr, " BEFORE: parent->child=%p\n", parent->child); + fprintf(stderr, " BEFORE: parent->last_child=%p\n", parent->last_child); + fprintf(stderr, " BEFORE: parent->prev=%p\n", parent->prev); + fprintf(stderr, " BEFORE: parent->next=%p\n", parent->next); + } +#endif /* DEBUG > 1 */ + + /* + * Remove the node from any existing parent... + */ + + if (node->parent) + mxmlRemove(node); + + /* + * Reset pointers... + */ + + node->parent = parent; + + switch (where) + { + case MXML_ADD_BEFORE : + if (!child || child == parent->child || child->parent != parent) + { + /* + * Insert as first node under parent... + */ + + node->next = parent->child; + + if (parent->child) + parent->child->prev = node; + else + parent->last_child = node; + + parent->child = node; + } + else + { + /* + * Insert node before this child... + */ + + node->next = child; + node->prev = child->prev; + + if (child->prev) + child->prev->next = node; + else + parent->child = node; + + child->prev = node; + } + break; + + case MXML_ADD_AFTER : + if (!child || child == parent->last_child || child->parent != parent) + { + /* + * Insert as last node under parent... + */ + + node->parent = parent; + node->prev = parent->last_child; + + if (parent->last_child) + parent->last_child->next = node; + else + parent->child = node; + + parent->last_child = node; + } + else + { + /* + * Insert node after this child... + */ + + node->prev = child; + node->next = child->next; + + if (child->next) + child->next->prev = node; + else + parent->last_child = node; + + child->next = node; + } + break; + } + +#if DEBUG > 1 + fprintf(stderr, " AFTER: node->parent=%p\n", node->parent); + if (parent) + { + fprintf(stderr, " AFTER: parent->child=%p\n", parent->child); + fprintf(stderr, " AFTER: parent->last_child=%p\n", parent->last_child); + fprintf(stderr, " AFTER: parent->prev=%p\n", parent->prev); + fprintf(stderr, " AFTER: parent->next=%p\n", parent->next); + } +#endif /* DEBUG > 1 */ +} + + +/* + * 'mxmlDelete()' - Delete a node and all of its children. + * + * If the specified node has a parent, this function first removes the + * node from its parent using the mxmlRemove() function. + */ + +void +mxmlDelete(mxml_node_t *node) /* I - Node to delete */ +{ + int i; /* Looping var */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlDelete(node=%p)\n", node); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!node) + return; + + /* + * Remove the node from its parent, if any... + */ + + mxmlRemove(node); + + /* + * Delete children... + */ + + while (node->child) + mxmlDelete(node->child); + + /* + * Now delete any node data... + */ + + switch (node->type) + { + case MXML_ELEMENT : + if (node->value.element.name) + free(node->value.element.name); + + if (node->value.element.num_attrs) + { + for (i = 0; i < node->value.element.num_attrs; i ++) + { + if (node->value.element.attrs[i].name) + free(node->value.element.attrs[i].name); + if (node->value.element.attrs[i].value) + free(node->value.element.attrs[i].value); + } + + free(node->value.element.attrs); + } + break; + case MXML_INTEGER : + /* Nothing to do */ + break; + case MXML_OPAQUE : + if (node->value.opaque) + free(node->value.opaque); + break; + case MXML_REAL : + /* Nothing to do */ + break; + case MXML_TEXT : + if (node->value.text.string) + free(node->value.text.string); + break; + case MXML_CUSTOM : + if (node->value.custom.data && + node->value.custom.destroy) + (*(node->value.custom.destroy))(node->value.custom.data); + break; + default : + break; + } + + /* + * Free this node... + */ + + free(node); +} + + +/* + * 'mxmlGetRefCount()' - Get the current reference (use) count for a node. + * + * The initial reference count of new nodes is 1. Use the @link mxmlRetain@ + * and @link mxmlRelease@ functions to increment and decrement a node's + * reference count. + * + * @since Mini-XML 2.7@. + */ + +int /* O - Reference count */ +mxmlGetRefCount(mxml_node_t *node) /* I - Node */ +{ + /* + * Range check input... + */ + + if (!node) + return (0); + + /* + * Return the reference count... + */ + + return (node->ref_count); +} + + +/* + * 'mxmlNewCDATA()' - Create a new CDATA node. + * + * The new CDATA node is added to the end of the specified parent's child + * list. The constant MXML_NO_PARENT can be used to specify that the new + * CDATA node has no parent. The data string must be nul-terminated and + * is copied into the new node. CDATA nodes use the MXML_ELEMENT type. + * + * @since Mini-XML 2.3@ + */ + +mxml_node_t * /* O - New node */ +mxmlNewCDATA(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ + const char *data) /* I - Data string */ +{ + mxml_node_t *node; /* New node */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlNewCDATA(parent=%p, data=\"%s\")\n", + parent, data ? data : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!data) + return (NULL); + + /* + * Create the node and set the name value... + */ + + if ((node = mxml_new(parent, MXML_ELEMENT)) != NULL) + node->value.element.name = _mxml_strdupf("![CDATA[%s]]", data); + + return (node); +} + + +/* + * 'mxmlNewCustom()' - Create a new custom data node. + * + * The new custom node is added to the end of the specified parent's child + * list. The constant MXML_NO_PARENT can be used to specify that the new + * element node has no parent. NULL can be passed when the data in the + * node is not dynamically allocated or is separately managed. + * + * @since Mini-XML 2.1@ + */ + +mxml_node_t * /* O - New node */ +mxmlNewCustom( + mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ + void *data, /* I - Pointer to data */ + mxml_custom_destroy_cb_t destroy) /* I - Function to destroy data */ +{ + mxml_node_t *node; /* New node */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlNewCustom(parent=%p, data=%p, destroy=%p)\n", parent, + data, destroy); +#endif /* DEBUG */ + + /* + * Create the node and set the value... + */ + + if ((node = mxml_new(parent, MXML_CUSTOM)) != NULL) + { + node->value.custom.data = data; + node->value.custom.destroy = destroy; + } + + return (node); +} + + +/* + * 'mxmlNewElement()' - Create a new element node. + * + * The new element node is added to the end of the specified parent's child + * list. The constant MXML_NO_PARENT can be used to specify that the new + * element node has no parent. + */ + +mxml_node_t * /* O - New node */ +mxmlNewElement(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ + const char *name) /* I - Name of element */ +{ + mxml_node_t *node; /* New node */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlNewElement(parent=%p, name=\"%s\")\n", parent, + name ? name : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!name) + return (NULL); + + /* + * Create the node and set the element name... + */ + + if ((node = mxml_new(parent, MXML_ELEMENT)) != NULL) + node->value.element.name = strdup(name); + + return (node); +} + + +/* + * 'mxmlNewInteger()' - Create a new integer node. + * + * The new integer node is added to the end of the specified parent's child + * list. The constant MXML_NO_PARENT can be used to specify that the new + * integer node has no parent. + */ + +mxml_node_t * /* O - New node */ +mxmlNewInteger(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ + int integer) /* I - Integer value */ +{ + mxml_node_t *node; /* New node */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlNewInteger(parent=%p, integer=%d)\n", parent, integer); +#endif /* DEBUG */ + + /* + * Create the node and set the element name... + */ + + if ((node = mxml_new(parent, MXML_INTEGER)) != NULL) + node->value.integer = integer; + + return (node); +} + + +/* + * 'mxmlNewOpaque()' - Create a new opaque string. + * + * The new opaque node is added to the end of the specified parent's child + * list. The constant MXML_NO_PARENT can be used to specify that the new + * opaque node has no parent. The opaque string must be nul-terminated and + * is copied into the new node. + */ + +mxml_node_t * /* O - New node */ +mxmlNewOpaque(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ + const char *opaque) /* I - Opaque string */ +{ + mxml_node_t *node; /* New node */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlNewOpaque(parent=%p, opaque=\"%s\")\n", parent, + opaque ? opaque : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!opaque) + return (NULL); + + /* + * Create the node and set the element name... + */ + + if ((node = mxml_new(parent, MXML_OPAQUE)) != NULL) + node->value.opaque = strdup(opaque); + + return (node); +} + + +/* + * 'mxmlNewReal()' - Create a new real number node. + * + * The new real number node is added to the end of the specified parent's + * child list. The constant MXML_NO_PARENT can be used to specify that + * the new real number node has no parent. + */ + +mxml_node_t * /* O - New node */ +mxmlNewReal(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ + double real) /* I - Real number value */ +{ + mxml_node_t *node; /* New node */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlNewReal(parent=%p, real=%g)\n", parent, real); +#endif /* DEBUG */ + + /* + * Create the node and set the element name... + */ + + if ((node = mxml_new(parent, MXML_REAL)) != NULL) + node->value.real = real; + + return (node); +} + + +/* + * 'mxmlNewText()' - Create a new text fragment node. + * + * The new text node is added to the end of the specified parent's child + * list. The constant MXML_NO_PARENT can be used to specify that the new + * text node has no parent. The whitespace parameter is used to specify + * whether leading whitespace is present before the node. The text + * string must be nul-terminated and is copied into the new node. + */ + +mxml_node_t * /* O - New node */ +mxmlNewText(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ + int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */ + const char *string) /* I - String */ +{ + mxml_node_t *node; /* New node */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlNewText(parent=%p, whitespace=%d, string=\"%s\")\n", + parent, whitespace, string ? string : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!string) + return (NULL); + + /* + * Create the node and set the text value... + */ + + if ((node = mxml_new(parent, MXML_TEXT)) != NULL) + { + node->value.text.whitespace = whitespace; + node->value.text.string = strdup(string); + } + + return (node); +} + + +/* + * 'mxmlNewTextf()' - Create a new formatted text fragment node. + * + * The new text node is added to the end of the specified parent's child + * list. The constant MXML_NO_PARENT can be used to specify that the new + * text node has no parent. The whitespace parameter is used to specify + * whether leading whitespace is present before the node. The format + * string must be nul-terminated and is formatted into the new node. + */ + +mxml_node_t * /* O - New node */ +mxmlNewTextf(mxml_node_t *parent, /* I - Parent node or MXML_NO_PARENT */ + int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */ + const char *format, /* I - Printf-style frmat string */ + ...) /* I - Additional args as needed */ +{ + mxml_node_t *node; /* New node */ + va_list ap; /* Pointer to arguments */ + + +#ifdef DEBUG + fprintf(stderr, "mxmlNewTextf(parent=%p, whitespace=%d, format=\"%s\", ...)\n", + parent, whitespace, format ? format : "(null)"); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!format) + return (NULL); + + /* + * Create the node and set the text value... + */ + + if ((node = mxml_new(parent, MXML_TEXT)) != NULL) + { + va_start(ap, format); + + node->value.text.whitespace = whitespace; + node->value.text.string = _mxml_vstrdupf(format, ap); + + va_end(ap); + } + + return (node); +} + + +/* + * 'mxmlRemove()' - Remove a node from its parent. + * + * Does not free memory used by the node - use mxmlDelete() for that. + * This function does nothing if the node has no parent. + */ + +void +mxmlRemove(mxml_node_t *node) /* I - Node to remove */ +{ +#ifdef DEBUG + fprintf(stderr, "mxmlRemove(node=%p)\n", node); +#endif /* DEBUG */ + + /* + * Range check input... + */ + + if (!node || !node->parent) + return; + + /* + * Remove from parent... + */ + +#if DEBUG > 1 + fprintf(stderr, " BEFORE: node->parent=%p\n", node->parent); + if (node->parent) + { + fprintf(stderr, " BEFORE: node->parent->child=%p\n", node->parent->child); + fprintf(stderr, " BEFORE: node->parent->last_child=%p\n", node->parent->last_child); + } + fprintf(stderr, " BEFORE: node->child=%p\n", node->child); + fprintf(stderr, " BEFORE: node->last_child=%p\n", node->last_child); + fprintf(stderr, " BEFORE: node->prev=%p\n", node->prev); + fprintf(stderr, " BEFORE: node->next=%p\n", node->next); +#endif /* DEBUG > 1 */ + + if (node->prev) + node->prev->next = node->next; + else + node->parent->child = node->next; + + if (node->next) + node->next->prev = node->prev; + else + node->parent->last_child = node->prev; + + node->parent = NULL; + node->prev = NULL; + node->next = NULL; + +#if DEBUG > 1 + fprintf(stderr, " AFTER: node->parent=%p\n", node->parent); + if (node->parent) + { + fprintf(stderr, " AFTER: node->parent->child=%p\n", node->parent->child); + fprintf(stderr, " AFTER: node->parent->last_child=%p\n", node->parent->last_child); + } + fprintf(stderr, " AFTER: node->child=%p\n", node->child); + fprintf(stderr, " AFTER: node->last_child=%p\n", node->last_child); + fprintf(stderr, " AFTER: node->prev=%p\n", node->prev); + fprintf(stderr, " AFTER: node->next=%p\n", node->next); +#endif /* DEBUG > 1 */ +} + + +/* + * 'mxmlNewXML()' - Create a new XML document tree. + * + * The "version" argument specifies the version number to put in the + * ?xml element node. If NULL, version 1.0 is assumed. + * + * @since Mini-XML 2.3@ + */ + +mxml_node_t * /* O - New ?xml node */ +mxmlNewXML(const char *version) /* I - Version number to use */ +{ + char element[1024]; /* Element text */ + + + snprintf(element, sizeof(element), "?xml version=\"%s\" encoding=\"utf-8\"?", + version ? version : "1.0"); + + return (mxmlNewElement(NULL, element)); +} + + +/* + * 'mxmlRelease()' - Release a node. + * + * When the reference count reaches zero, the node (and any children) + * is deleted via mxmlDelete(). + * + * @since Mini-XML 2.3@ + */ + +int /* O - New reference count */ +mxmlRelease(mxml_node_t *node) /* I - Node */ +{ + if (node) + { + if ((-- node->ref_count) <= 0) + { + mxmlDelete(node); + return (0); + } + else + return (node->ref_count); + } + else + return (-1); +} + + +/* + * 'mxmlRetain()' - Retain a node. + * + * @since Mini-XML 2.3@ + */ + +int /* O - New reference count */ +mxmlRetain(mxml_node_t *node) /* I - Node */ +{ + if (node) + return (++ node->ref_count); + else + return (-1); +} + + +/* + * 'mxml_new()' - Create a new node. + */ + +static mxml_node_t * /* O - New node */ +mxml_new(mxml_node_t *parent, /* I - Parent node */ + mxml_type_t type) /* I - Node type */ +{ + mxml_node_t *node; /* New node */ + + +#if DEBUG > 1 + fprintf(stderr, "mxml_new(parent=%p, type=%d)\n", parent, type); +#endif /* DEBUG > 1 */ + + /* + * Allocate memory for the node... + */ + + if ((node = calloc(1, sizeof(mxml_node_t))) == NULL) + { +#if DEBUG > 1 + fputs(" returning NULL\n", stderr); +#endif /* DEBUG > 1 */ + + return (NULL); + } + +#if DEBUG > 1 + fprintf(stderr, " returning %p\n", node); +#endif /* DEBUG > 1 */ + + /* + * Set the node type... + */ + + node->type = type; + node->ref_count = 1; + + /* + * Add to the parent if present... + */ + + if (parent) + mxmlAdd(parent, MXML_ADD_AFTER, MXML_ADD_TO_PARENT, node); + + /* + * Return the new node... + */ + + return (node); +} + + +/* + * End of "$Id: mxml-node.c 436 2011-01-22 01:02:05Z mike $". + */ diff --git a/daemon/mxml/mxml-private.c b/daemon/mxml/mxml-private.c new file mode 100644 index 0000000..72f3e23 --- /dev/null +++ b/daemon/mxml/mxml-private.c @@ -0,0 +1,331 @@ +/* + * "$Id: mxml-private.c 422 2010-11-07 22:55:11Z mike $" + * + * Private functions for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2010 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + * mxml_error() - Display an error message. + * mxml_integer_cb() - Default callback for integer values. + * mxml_opaque_cb() - Default callback for opaque values. + * mxml_real_cb() - Default callback for real number values. + * _mxml_global() - Get global data. + */ + +/* + * Include necessary headers... + */ + +#include "mxml-private.h" + + +/* + * Some crazy people think that unloading a shared object is a good or safe + * thing to do. Unfortunately, most objects are simply *not* safe to unload + * and bad things *will* happen. + * + * The following mess of conditional code allows us to provide a destructor + * function in Mini-XML for our thread-global storage so that it can possibly + * be unloaded safely, although since there is no standard way to do so I + * can't even provide any guarantees that you can do it safely on all platforms. + * + * This code currently supports AIX, HP-UX, Linux, Mac OS X, Solaris, and + * Windows. It might work on the BSDs and IRIX, but I haven't tested that. + */ + +#if defined(__sun) || defined(_AIX) +# pragma fini(_mxml_fini) +# define _MXML_FINI _mxml_fini +#elif defined(__hpux) +# pragma FINI _mxml_fini +# define _MXML_FINI _mxml_fini +#elif defined(__GNUC__) /* Linux and Mac OS X */ +# define _MXML_FINI __attribute((destructor)) _mxml_fini +#else +# define _MXML_FINI _fini +#endif /* __sun */ + + +/* + * 'mxml_error()' - Display an error message. + */ + +void +mxml_error(const char *format, /* I - Printf-style format string */ + ...) /* I - Additional arguments as needed */ +{ + va_list ap; /* Pointer to arguments */ + char s[1024]; /* Message string */ + _mxml_global_t *global = _mxml_global(); + /* Global data */ + + + /* + * Range check input... + */ + + if (!format) + return; + + /* + * Format the error message string... + */ + + va_start(ap, format); + + vsnprintf(s, sizeof(s), format, ap); + + va_end(ap); + + /* + * And then display the error message... + */ + + if (global->error_cb) + (*global->error_cb)(s); + else + fprintf(stderr, "mxml: %s\n", s); +} + + +/* + * 'mxml_ignore_cb()' - Default callback for ignored values. + */ + +mxml_type_t /* O - Node type */ +mxml_ignore_cb(mxml_node_t *node) /* I - Current node */ +{ + (void)node; + + return (MXML_IGNORE); +} + + +/* + * 'mxml_integer_cb()' - Default callback for integer values. + */ + +mxml_type_t /* O - Node type */ +mxml_integer_cb(mxml_node_t *node) /* I - Current node */ +{ + (void)node; + + return (MXML_INTEGER); +} + + +/* + * 'mxml_opaque_cb()' - Default callback for opaque values. + */ + +mxml_type_t /* O - Node type */ +mxml_opaque_cb(mxml_node_t *node) /* I - Current node */ +{ + (void)node; + + return (MXML_OPAQUE); +} + + +/* + * 'mxml_real_cb()' - Default callback for real number values. + */ + +mxml_type_t /* O - Node type */ +mxml_real_cb(mxml_node_t *node) /* I - Current node */ +{ + (void)node; + + return (MXML_REAL); +} + + +#ifdef HAVE_PTHREAD_H /**** POSIX threading ****/ +# include + +static pthread_key_t _mxml_key = -1; /* Thread local storage key */ +static pthread_once_t _mxml_key_once = PTHREAD_ONCE_INIT; + /* One-time initialization object */ +static void _mxml_init(void); +static void _mxml_destructor(void *g); + + +/* + * '_mxml_destructor()' - Free memory used for globals... + */ + +static void +_mxml_destructor(void *g) /* I - Global data */ +{ + free(g); +} + + +/* + * '_mxml_fini()' - Clean up when unloaded. + */ + +static void +_MXML_FINI(void) +{ + _mxml_global_t *global; /* Global data */ + + + if (_mxml_key != -1) + { + if ((global = (_mxml_global_t *)pthread_getspecific(_mxml_key)) != NULL) + _mxml_destructor(global); + + pthread_key_delete(_mxml_key); + _mxml_key = -1; + } +} + + +/* + * '_mxml_global()' - Get global data. + */ + +_mxml_global_t * /* O - Global data */ +_mxml_global(void) +{ + _mxml_global_t *global; /* Global data */ + + + pthread_once(&_mxml_key_once, _mxml_init); + + if ((global = (_mxml_global_t *)pthread_getspecific(_mxml_key)) == NULL) + { + global = (_mxml_global_t *)calloc(1, sizeof(_mxml_global_t)); + pthread_setspecific(_mxml_key, global); + + global->num_entity_cbs = 1; + global->entity_cbs[0] = _mxml_entity_cb; + global->wrap = 72; + } + + return (global); +} + + +/* + * '_mxml_init()' - Initialize global data... + */ + +static void +_mxml_init(void) +{ + pthread_key_create(&_mxml_key, _mxml_destructor); +} + + +#elif defined(WIN32) && defined(MXML1_EXPORTS) /**** WIN32 threading ****/ +# include + +static DWORD _mxml_tls_index; /* Index for global storage */ + + +/* + * 'DllMain()' - Main entry for library. + */ + +BOOL WINAPI /* O - Success/failure */ +DllMain(HINSTANCE hinst, /* I - DLL module handle */ + DWORD reason, /* I - Reason */ + LPVOID reserved) /* I - Unused */ +{ + _mxml_global_t *global; /* Global data */ + + + (void)hinst; + (void)reserved; + + switch (reason) + { + case DLL_PROCESS_ATTACH : /* Called on library initialization */ + if ((_mxml_tls_index = TlsAlloc()) == TLS_OUT_OF_INDEXES) + return (FALSE); + break; + + case DLL_THREAD_DETACH : /* Called when a thread terminates */ + if ((global = (_mxml_global_t *)TlsGetValue(_mxml_tls_index)) != NULL) + free(global); + break; + + case DLL_PROCESS_DETACH : /* Called when library is unloaded */ + if ((global = (_mxml_global_t *)TlsGetValue(_mxml_tls_index)) != NULL) + free(global); + + TlsFree(_mxml_tls_index); + break; + + default: + break; + } + + return (TRUE); +} + + +/* + * '_mxml_global()' - Get global data. + */ + +_mxml_global_t * /* O - Global data */ +_mxml_global(void) +{ + _mxml_global_t *global; /* Global data */ + + + if ((global = (_mxml_global_t *)TlsGetValue(_mxml_tls_index)) == NULL) + { + global = (_mxml_global_t *)calloc(1, sizeof(_mxml_global_t)); + + global->num_entity_cbs = 1; + global->entity_cbs[0] = _mxml_entity_cb; + global->wrap = 72; + + TlsSetValue(_mxml_tls_index, (LPVOID)global); + } + + return (global); +} + + +#else /**** No threading ****/ +/* + * '_mxml_global()' - Get global data. + */ + +_mxml_global_t * /* O - Global data */ +_mxml_global(void) +{ + static _mxml_global_t global = /* Global data */ + { + NULL, /* error_cb */ + 1, /* num_entity_cbs */ + { _mxml_entity_cb }, /* entity_cbs */ + 72, /* wrap */ + NULL, /* custom_load_cb */ + NULL /* custom_save_cb */ + }; + + + return (&global); +} +#endif /* HAVE_PTHREAD_H */ + + +/* + * End of "$Id: mxml-private.c 422 2010-11-07 22:55:11Z mike $". + */ diff --git a/daemon/mxml/mxml-private.h b/daemon/mxml/mxml-private.h new file mode 100644 index 0000000..8789e6c --- /dev/null +++ b/daemon/mxml/mxml-private.h @@ -0,0 +1,50 @@ +/* + * "$Id: mxml-private.h 408 2010-09-19 05:26:46Z mike $" + * + * Private definitions for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2010 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + */ + +/* + * Include necessary headers... + */ + +#include "config.h" +#include "mxml.h" + + +/* + * Global, per-thread data... + */ + +typedef struct _mxml_global_s +{ + void (*error_cb)(const char *); + int num_entity_cbs; + int (*entity_cbs[100])(const char *name); + int wrap; + mxml_custom_load_cb_t custom_load_cb; + mxml_custom_save_cb_t custom_save_cb; +} _mxml_global_t; + + +/* + * Functions... + */ + +extern _mxml_global_t *_mxml_global(void); +extern int _mxml_entity_cb(const char *name); + + +/* + * End of "$Id: mxml-private.h 408 2010-09-19 05:26:46Z mike $". + */ diff --git a/daemon/mxml/mxml-search.c b/daemon/mxml/mxml-search.c new file mode 100644 index 0000000..f975af1 --- /dev/null +++ b/daemon/mxml/mxml-search.c @@ -0,0 +1,287 @@ +/* + * "$Id: mxml-search.c 427 2011-01-03 02:03:29Z mike $" + * + * Search/navigation functions for Mini-XML, a small XML-like file + * parsing library. + * + * Copyright 2003-2010 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + * mxmlFindElement() - Find the named element. + * mxmlFindValue() - Find a value with the given path. + * mxmlWalkNext() - Walk to the next logical node in the tree. + * mxmlWalkPrev() - Walk to the previous logical node in the tree. + */ + +/* + * Include necessary headers... + */ + +#include "config.h" +#include "mxml.h" + + +/* + * 'mxmlFindElement()' - Find the named element. + * + * The search is constrained by the name, attribute name, and value; any + * NULL names or values are treated as wildcards, so different kinds of + * searches can be implemented by looking for all elements of a given name + * or all elements with a specific attribute. The descend argument determines + * whether the search descends into child nodes; normally you will use + * MXML_DESCEND_FIRST for the initial search and MXML_NO_DESCEND to find + * additional direct descendents of the node. The top node argument + * constrains the search to a particular node's children. + */ + +mxml_node_t * /* O - Element node or NULL */ +mxmlFindElement(mxml_node_t *node, /* I - Current node */ + mxml_node_t *top, /* I - Top node */ + const char *name, /* I - Element name or NULL for any */ + const char *attr, /* I - Attribute name, or NULL for none */ + const char *value, /* I - Attribute value, or NULL for any */ + int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */ +{ + const char *temp; /* Current attribute value */ + + + /* + * Range check input... + */ + + if (!node || !top || (!attr && value)) + return (NULL); + + /* + * Start with the next node... + */ + + node = mxmlWalkNext(node, top, descend); + + /* + * Loop until we find a matching element... + */ + + while (node != NULL) + { + /* + * See if this node matches... + */ + + if (node->type == MXML_ELEMENT && + node->value.element.name && + (!name || !strcmp(node->value.element.name, name))) + { + /* + * See if we need to check for an attribute... + */ + + if (!attr) + return (node); /* No attribute search, return it... */ + + /* + * Check for the attribute... + */ + + if ((temp = mxmlElementGetAttr(node, attr)) != NULL) + { + /* + * OK, we have the attribute, does it match? + */ + + if (!value || !strcmp(value, temp)) + return (node); /* Yes, return it... */ + } + } + + /* + * No match, move on to the next node... + */ + + if (descend == MXML_DESCEND) + node = mxmlWalkNext(node, top, MXML_DESCEND); + else + node = node->next; + } + + return (NULL); +} + + +/* + * 'mxmlFindPath()' - Find a node with the given path. + * + * The "path" is a slash-separated list of element names. The name "*" is + * considered a wildcard for one or more levels of elements. For example, + * "foo/one/two", "bar/two/one", "*\/one", and so forth. + * + * The first child node of the found node is returned if the given node has + * children and the first child is a value node. + * + * @since Mini-XML 2.7@ + */ + +mxml_node_t * /* O - Found node or NULL */ +mxmlFindPath(mxml_node_t *top, /* I - Top node */ + const char *path) /* I - Path to element */ +{ + mxml_node_t *node; /* Current node */ + char element[256]; /* Current element name */ + const char *pathsep; /* Separator in path */ + int descend; /* mxmlFindElement option */ + + + /* + * Range check input... + */ + + if (!top || !path || !*path) + return (NULL); + + /* + * Search each element in the path... + */ + + node = top; + while (*path) + { + /* + * Handle wildcards... + */ + + if (!strncmp(path, "*/", 2)) + { + path += 2; + descend = MXML_DESCEND; + } + else + descend = MXML_DESCEND_FIRST; + + /* + * Get the next element in the path... + */ + + if ((pathsep = strchr(path, '/')) == NULL) + pathsep = path + strlen(path); + + if (pathsep == path || (pathsep - path) >= sizeof(element)) + return (NULL); + + memcpy(element, path, pathsep - path); + element[pathsep - path] = '\0'; + + if (*pathsep) + path = pathsep + 1; + else + path = pathsep; + + /* + * Search for the element... + */ + + if ((node = mxmlFindElement(node, node, element, NULL, NULL, + descend)) == NULL) + return (NULL); + } + + /* + * If we get this far, return the node or its first child... + */ + + if (node->child && node->child->type != MXML_ELEMENT) + return (node->child); + else + return (node); +} + + +/* + * 'mxmlWalkNext()' - Walk to the next logical node in the tree. + * + * The descend argument controls whether the first child is considered + * to be the next node. The top node argument constrains the walk to + * the node's children. + */ + +mxml_node_t * /* O - Next node or NULL */ +mxmlWalkNext(mxml_node_t *node, /* I - Current node */ + mxml_node_t *top, /* I - Top node */ + int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */ +{ + if (!node) + return (NULL); + else if (node->child && descend) + return (node->child); + else if (node == top) + return (NULL); + else if (node->next) + return (node->next); + else if (node->parent && node->parent != top) + { + node = node->parent; + + while (!node->next) + if (node->parent == top || !node->parent) + return (NULL); + else + node = node->parent; + + return (node->next); + } + else + return (NULL); +} + + +/* + * 'mxmlWalkPrev()' - Walk to the previous logical node in the tree. + * + * The descend argument controls whether the previous node's last child + * is considered to be the previous node. The top node argument constrains + * the walk to the node's children. + */ + +mxml_node_t * /* O - Previous node or NULL */ +mxmlWalkPrev(mxml_node_t *node, /* I - Current node */ + mxml_node_t *top, /* I - Top node */ + int descend) /* I - Descend into tree - MXML_DESCEND, MXML_NO_DESCEND, or MXML_DESCEND_FIRST */ +{ + if (!node || node == top) + return (NULL); + else if (node->prev) + { + if (node->prev->last_child && descend) + { + /* + * Find the last child under the previous node... + */ + + node = node->prev->last_child; + + while (node->last_child) + node = node->last_child; + + return (node); + } + else + return (node->prev); + } + else if (node->parent != top) + return (node->parent); + else + return (NULL); +} + + +/* + * End of "$Id: mxml-search.c 427 2011-01-03 02:03:29Z mike $". + */ diff --git a/daemon/mxml/mxml-set.c b/daemon/mxml/mxml-set.c new file mode 100644 index 0000000..b0bd527 --- /dev/null +++ b/daemon/mxml/mxml-set.c @@ -0,0 +1,349 @@ +/* + * "$Id: mxml-set.c 441 2011-12-09 23:49:00Z mike $" + * + * Node set functions for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2011 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + * mxmlSetCDATA() - Set the element name of a CDATA node. + * mxmlSetCustom() - Set the data and destructor of a custom data node. + * mxmlSetElement() - Set the name of an element node. + * mxmlSetInteger() - Set the value of an integer node. + * mxmlSetOpaque() - Set the value of an opaque node. + * mxmlSetReal() - Set the value of a real number node. + * mxmlSetText() - Set the value of a text node. + * mxmlSetTextf() - Set the value of a text node to a formatted string. + * mxmlSetUserData() - Set the user data pointer for a node. + */ + +/* + * Include necessary headers... + */ + +#include "config.h" +#include "mxml.h" + + +/* + * 'mxmlSetCDATA()' - Set the element name of a CDATA node. + * + * The node is not changed if it (or its first child) is not a CDATA element node. + * + * @since Mini-XML 2.3@ + */ + +int /* O - 0 on success, -1 on failure */ +mxmlSetCDATA(mxml_node_t *node, /* I - Node to set */ + const char *data) /* I - New data string */ +{ + /* + * Range check input... + */ + + if (node && node->type == MXML_ELEMENT && + strncmp(node->value.element.name, "![CDATA[", 8) && + node->child && node->child->type == MXML_ELEMENT && + !strncmp(node->child->value.element.name, "![CDATA[", 8)) + node = node->child; + + if (!node || node->type != MXML_ELEMENT || !data || + strncmp(node->value.element.name, "![CDATA[", 8)) + return (-1); + + /* + * Free any old element value and set the new value... + */ + + if (node->value.element.name) + free(node->value.element.name); + + node->value.element.name = _mxml_strdupf("![CDATA[%s]]", data); + + return (0); +} + + +/* + * 'mxmlSetCustom()' - Set the data and destructor of a custom data node. + * + * The node is not changed if it (or its first child) is not a custom node. + * + * @since Mini-XML 2.1@ + */ + +int /* O - 0 on success, -1 on failure */ +mxmlSetCustom( + mxml_node_t *node, /* I - Node to set */ + void *data, /* I - New data pointer */ + mxml_custom_destroy_cb_t destroy) /* I - New destructor function */ +{ + /* + * Range check input... + */ + + if (node && node->type == MXML_ELEMENT && + node->child && node->child->type == MXML_CUSTOM) + node = node->child; + + if (!node || node->type != MXML_CUSTOM) + return (-1); + + /* + * Free any old element value and set the new value... + */ + + if (node->value.custom.data && node->value.custom.destroy) + (*(node->value.custom.destroy))(node->value.custom.data); + + node->value.custom.data = data; + node->value.custom.destroy = destroy; + + return (0); +} + + +/* + * 'mxmlSetElement()' - Set the name of an element node. + * + * The node is not changed if it is not an element node. + */ + +int /* O - 0 on success, -1 on failure */ +mxmlSetElement(mxml_node_t *node, /* I - Node to set */ + const char *name) /* I - New name string */ +{ + /* + * Range check input... + */ + + if (!node || node->type != MXML_ELEMENT || !name) + return (-1); + + /* + * Free any old element value and set the new value... + */ + + if (node->value.element.name) + free(node->value.element.name); + + node->value.element.name = strdup(name); + + return (0); +} + + +/* + * 'mxmlSetInteger()' - Set the value of an integer node. + * + * The node is not changed if it (or its first child) is not an integer node. + */ + +int /* O - 0 on success, -1 on failure */ +mxmlSetInteger(mxml_node_t *node, /* I - Node to set */ + int integer) /* I - Integer value */ +{ + /* + * Range check input... + */ + + if (node && node->type == MXML_ELEMENT && + node->child && node->child->type == MXML_INTEGER) + node = node->child; + + if (!node || node->type != MXML_INTEGER) + return (-1); + + /* + * Set the new value and return... + */ + + node->value.integer = integer; + + return (0); +} + + +/* + * 'mxmlSetOpaque()' - Set the value of an opaque node. + * + * The node is not changed if it (or its first child) is not an opaque node. + */ + +int /* O - 0 on success, -1 on failure */ +mxmlSetOpaque(mxml_node_t *node, /* I - Node to set */ + const char *opaque) /* I - Opaque string */ +{ + /* + * Range check input... + */ + + if (node && node->type == MXML_ELEMENT && + node->child && node->child->type == MXML_OPAQUE) + node = node->child; + + if (!node || node->type != MXML_OPAQUE || !opaque) + return (-1); + + /* + * Free any old opaque value and set the new value... + */ + + if (node->value.opaque) + free(node->value.opaque); + + node->value.opaque = strdup(opaque); + + return (0); +} + + +/* + * 'mxmlSetReal()' - Set the value of a real number node. + * + * The node is not changed if it (or its first child) is not a real number node. + */ + +int /* O - 0 on success, -1 on failure */ +mxmlSetReal(mxml_node_t *node, /* I - Node to set */ + double real) /* I - Real number value */ +{ + /* + * Range check input... + */ + + if (node && node->type == MXML_ELEMENT && + node->child && node->child->type == MXML_REAL) + node = node->child; + + if (!node || node->type != MXML_REAL) + return (-1); + + /* + * Set the new value and return... + */ + + node->value.real = real; + + return (0); +} + + +/* + * 'mxmlSetText()' - Set the value of a text node. + * + * The node is not changed if it (or its first child) is not a text node. + */ + +int /* O - 0 on success, -1 on failure */ +mxmlSetText(mxml_node_t *node, /* I - Node to set */ + int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */ + const char *string) /* I - String */ +{ + /* + * Range check input... + */ + + if (node && node->type == MXML_ELEMENT && + node->child && node->child->type == MXML_TEXT) + node = node->child; + + if (!node || node->type != MXML_TEXT || !string) + return (-1); + + /* + * Free any old string value and set the new value... + */ + + if (node->value.text.string) + free(node->value.text.string); + + node->value.text.whitespace = whitespace; + node->value.text.string = strdup(string); + + return (0); +} + + +/* + * 'mxmlSetTextf()' - Set the value of a text node to a formatted string. + * + * The node is not changed if it (or its first child) is not a text node. + */ + +int /* O - 0 on success, -1 on failure */ +mxmlSetTextf(mxml_node_t *node, /* I - Node to set */ + int whitespace, /* I - 1 = leading whitespace, 0 = no whitespace */ + const char *format, /* I - Printf-style format string */ + ...) /* I - Additional arguments as needed */ +{ + va_list ap; /* Pointer to arguments */ + + + /* + * Range check input... + */ + + if (node && node->type == MXML_ELEMENT && + node->child && node->child->type == MXML_TEXT) + node = node->child; + + if (!node || node->type != MXML_TEXT || !format) + return (-1); + + /* + * Free any old string value and set the new value... + */ + + if (node->value.text.string) + free(node->value.text.string); + + va_start(ap, format); + + node->value.text.whitespace = whitespace; + node->value.text.string = _mxml_strdupf(format, ap); + + va_end(ap); + + return (0); +} + + +/* + * 'mxmlSetUserData()' - Set the user data pointer for a node. + * + * @since Mini-XML 2.7@ + */ + +int /* O - 0 on success, -1 on failure */ +mxmlSetUserData(mxml_node_t *node, /* I - Node to set */ + void *data) /* I - User data pointer */ +{ + /* + * Range check input... + */ + + if (!node) + return (-1); + + /* + * Set the user data pointer and return... + */ + + node->user_data = data; + return (0); +} + + +/* + * End of "$Id: mxml-set.c 441 2011-12-09 23:49:00Z mike $". + */ diff --git a/daemon/mxml/mxml-string.c b/daemon/mxml/mxml-string.c new file mode 100644 index 0000000..6be4252 --- /dev/null +++ b/daemon/mxml/mxml-string.c @@ -0,0 +1,476 @@ +/* + * "$Id: mxml-string.c 424 2010-12-25 16:21:50Z mike $" + * + * String functions for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2010 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + * + * Contents: + * + * _mxml_snprintf() - Format a string. + * _mxml_strdup() - Duplicate a string. + * _mxml_strdupf() - Format and duplicate a string. + * _mxml_vsnprintf() - Format a string into a fixed size buffer. + * _mxml_vstrdupf() - Format and duplicate a string. + */ + +/* + * Include necessary headers... + */ + +#include "config.h" + + +/* + * The va_copy macro is part of C99, but many compilers don't implement it. + * Provide a "direct assignment" implmentation when va_copy isn't defined... + */ + +#ifndef va_copy +# ifdef __va_copy +# define va_copy(dst,src) __va_copy(dst,src) +# else +# define va_copy(dst,src) memcpy(&dst, &src, sizeof(va_list)) +# endif /* __va_copy */ +#endif /* va_copy */ + + +#ifndef HAVE_SNPRINTF +/* + * '_mxml_snprintf()' - Format a string. + */ + +int /* O - Number of bytes formatted */ +_mxml_snprintf(char *buffer, /* I - Output buffer */ + size_t bufsize, /* I - Size of output buffer */ + const char *format, /* I - Printf-style format string */ + ...) /* I - Additional arguments as needed */ +{ + va_list ap; /* Argument list */ + int bytes; /* Number of bytes formatted */ + + + va_start(ap, format); + bytes = vsnprintf(buffer, bufsize, format, ap); + va_end(ap); + + return (bytes); +} +#endif /* !HAVE_SNPRINTF */ + + +/* + * '_mxml_strdup()' - Duplicate a string. + */ + +#ifndef HAVE_STRDUP +char * /* O - New string pointer */ +_mxml_strdup(const char *s) /* I - String to duplicate */ +{ + char *t; /* New string pointer */ + + + if (s == NULL) + return (NULL); + + if ((t = malloc(strlen(s) + 1)) == NULL) + return (NULL); + + return (strcpy(t, s)); +} +#endif /* !HAVE_STRDUP */ + + +/* + * '_mxml_strdupf()' - Format and duplicate a string. + */ + +char * /* O - New string pointer */ +_mxml_strdupf(const char *format, /* I - Printf-style format string */ + ...) /* I - Additional arguments as needed */ +{ + va_list ap; /* Pointer to additional arguments */ + char *s; /* Pointer to formatted string */ + + + /* + * Get a pointer to the additional arguments, format the string, + * and return it... + */ + + va_start(ap, format); + s = _mxml_vstrdupf(format, ap); + va_end(ap); + + return (s); +} + + +#ifndef HAVE_VSNPRINTF +/* + * '_mxml_vsnprintf()' - Format a string into a fixed size buffer. + */ + +int /* O - Number of bytes formatted */ +_mxml_vsnprintf(char *buffer, /* O - Output buffer */ + size_t bufsize, /* O - Size of output buffer */ + const char *format, /* I - Printf-style format string */ + va_list ap) /* I - Pointer to additional arguments */ +{ + char *bufptr, /* Pointer to position in buffer */ + *bufend, /* Pointer to end of buffer */ + sign, /* Sign of format width */ + size, /* Size character (h, l, L) */ + type; /* Format type character */ + int width, /* Width of field */ + prec; /* Number of characters of precision */ + char tformat[100], /* Temporary format string for sprintf() */ + *tptr, /* Pointer into temporary format */ + temp[1024]; /* Buffer for formatted numbers */ + char *s; /* Pointer to string */ + int slen; /* Length of string */ + int bytes; /* Total number of bytes needed */ + + + /* + * Loop through the format string, formatting as needed... + */ + + bufptr = buffer; + bufend = buffer + bufsize - 1; + bytes = 0; + + while (*format) + { + if (*format == '%') + { + tptr = tformat; + *tptr++ = *format++; + + if (*format == '%') + { + if (bufptr && bufptr < bufend) *bufptr++ = *format; + bytes ++; + format ++; + continue; + } + else if (strchr(" -+#\'", *format)) + { + *tptr++ = *format; + sign = *format++; + } + else + sign = 0; + + if (*format == '*') + { + /* + * Get width from argument... + */ + + format ++; + width = va_arg(ap, int); + + snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", width); + tptr += strlen(tptr); + } + else + { + width = 0; + + while (isdigit(*format & 255)) + { + if (tptr < (tformat + sizeof(tformat) - 1)) + *tptr++ = *format; + + width = width * 10 + *format++ - '0'; + } + } + + if (*format == '.') + { + if (tptr < (tformat + sizeof(tformat) - 1)) + *tptr++ = *format; + + format ++; + + if (*format == '*') + { + /* + * Get precision from argument... + */ + + format ++; + prec = va_arg(ap, int); + + snprintf(tptr, sizeof(tformat) - (tptr - tformat), "%d", prec); + tptr += strlen(tptr); + } + else + { + prec = 0; + + while (isdigit(*format & 255)) + { + if (tptr < (tformat + sizeof(tformat) - 1)) + *tptr++ = *format; + + prec = prec * 10 + *format++ - '0'; + } + } + } + else + prec = -1; + + if (*format == 'l' && format[1] == 'l') + { + size = 'L'; + + if (tptr < (tformat + sizeof(tformat) - 2)) + { + *tptr++ = 'l'; + *tptr++ = 'l'; + } + + format += 2; + } + else if (*format == 'h' || *format == 'l' || *format == 'L') + { + if (tptr < (tformat + sizeof(tformat) - 1)) + *tptr++ = *format; + + size = *format++; + } + + if (!*format) + break; + + if (tptr < (tformat + sizeof(tformat) - 1)) + *tptr++ = *format; + + type = *format++; + *tptr = '\0'; + + switch (type) + { + case 'E' : /* Floating point formats */ + case 'G' : + case 'e' : + case 'f' : + case 'g' : + if ((width + 2) > sizeof(temp)) + break; + + sprintf(temp, tformat, va_arg(ap, double)); + + bytes += strlen(temp); + + if (bufptr) + { + if ((bufptr + strlen(temp)) > bufend) + { + strncpy(bufptr, temp, (size_t)(bufend - bufptr)); + bufptr = bufend; + } + else + { + strcpy(bufptr, temp); + bufptr += strlen(temp); + } + } + break; + + case 'B' : /* Integer formats */ + case 'X' : + case 'b' : + case 'd' : + case 'i' : + case 'o' : + case 'u' : + case 'x' : + if ((width + 2) > sizeof(temp)) + break; + +#ifdef HAVE_LONG_LONG + if (size == 'L') + sprintf(temp, tformat, va_arg(ap, long long)); + else +#endif /* HAVE_LONG_LONG */ + sprintf(temp, tformat, va_arg(ap, int)); + + bytes += strlen(temp); + + if (bufptr) + { + if ((bufptr + strlen(temp)) > bufend) + { + strncpy(bufptr, temp, (size_t)(bufend - bufptr)); + bufptr = bufend; + } + else + { + strcpy(bufptr, temp); + bufptr += strlen(temp); + } + } + break; + + case 'p' : /* Pointer value */ + if ((width + 2) > sizeof(temp)) + break; + + sprintf(temp, tformat, va_arg(ap, void *)); + + bytes += strlen(temp); + + if (bufptr) + { + if ((bufptr + strlen(temp)) > bufend) + { + strncpy(bufptr, temp, (size_t)(bufend - bufptr)); + bufptr = bufend; + } + else + { + strcpy(bufptr, temp); + bufptr += strlen(temp); + } + } + break; + + case 'c' : /* Character or character array */ + bytes += width; + + if (bufptr) + { + if (width <= 1) + *bufptr++ = va_arg(ap, int); + else + { + if ((bufptr + width) > bufend) + width = bufend - bufptr; + + memcpy(bufptr, va_arg(ap, char *), (size_t)width); + bufptr += width; + } + } + break; + + case 's' : /* String */ + if ((s = va_arg(ap, char *)) == NULL) + s = "(null)"; + + slen = strlen(s); + if (slen > width && prec != width) + width = slen; + + bytes += width; + + if (bufptr) + { + if ((bufptr + width) > bufend) + width = bufend - bufptr; + + if (slen > width) + slen = width; + + if (sign == '-') + { + strncpy(bufptr, s, (size_t)slen); + memset(bufptr + slen, ' ', (size_t)(width - slen)); + } + else + { + memset(bufptr, ' ', (size_t)(width - slen)); + strncpy(bufptr + width - slen, s, (size_t)slen); + } + + bufptr += width; + } + break; + + case 'n' : /* Output number of chars so far */ + *(va_arg(ap, int *)) = bytes; + break; + } + } + else + { + bytes ++; + + if (bufptr && bufptr < bufend) + *bufptr++ = *format; + + format ++; + } + } + + /* + * Nul-terminate the string and return the number of characters needed. + */ + + *bufptr = '\0'; + + return (bytes); +} +#endif /* !HAVE_VSNPRINTF */ + + +/* + * '_mxml_vstrdupf()' - Format and duplicate a string. + */ + +char * /* O - New string pointer */ +_mxml_vstrdupf(const char *format, /* I - Printf-style format string */ + va_list ap) /* I - Pointer to additional arguments */ +{ + int bytes; /* Number of bytes required */ + char *buffer, /* String buffer */ + temp[256]; /* Small buffer for first vsnprintf */ + va_list apcopy; /* Copy of argument list */ + + + /* + * First format with a tiny buffer; this will tell us how many bytes are + * needed... + */ + + va_copy(apcopy, ap); + bytes = vsnprintf(temp, sizeof(temp), format, apcopy); + + if (bytes < sizeof(temp)) + { + /* + * Hey, the formatted string fits in the tiny buffer, so just dup that... + */ + + return (strdup(temp)); + } + + /* + * Allocate memory for the whole thing and reformat to the new, larger + * buffer... + */ + + if ((buffer = calloc(1, bytes + 1)) != NULL) + vsnprintf(buffer, bytes + 1, format, ap); + + /* + * Return the new string... + */ + + return (buffer); +} + + +/* + * End of "$Id: mxml-string.c 424 2010-12-25 16:21:50Z mike $". + */ diff --git a/daemon/mxml/mxml.h b/daemon/mxml/mxml.h new file mode 100644 index 0000000..79c711f --- /dev/null +++ b/daemon/mxml/mxml.h @@ -0,0 +1,329 @@ +/* + * "$Id: mxml.h 427 2011-01-03 02:03:29Z mike $" + * + * Header file for Mini-XML, a small XML-like file parsing library. + * + * Copyright 2003-2011 by Michael R Sweet. + * + * These coded instructions, statements, and computer programs are the + * property of Michael R Sweet and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "COPYING" + * which should have been included with this file. If this file is + * missing or damaged, see the license at: + * + * http://www.minixml.org/ + */ + +/* + * Prevent multiple inclusion... + */ + +#ifndef _mxml_h_ +# define _mxml_h_ + +/* + * Include necessary headers... + */ + +# include +# include +# include +# include +# include + + +/* + * Constants... + */ + +# define MXML_TAB 8 /* Tabs every N columns */ + +# define MXML_NO_CALLBACK 0 /* Don't use a type callback */ +# define MXML_INTEGER_CALLBACK mxml_integer_cb + /* Treat all data as integers */ +# define MXML_OPAQUE_CALLBACK mxml_opaque_cb + /* Treat all data as opaque */ +# define MXML_REAL_CALLBACK mxml_real_cb + /* Treat all data as real numbers */ +# define MXML_TEXT_CALLBACK 0 /* Treat all data as text */ +# define MXML_IGNORE_CALLBACK mxml_ignore_cb + /* Ignore all non-element content */ + +# define MXML_NO_PARENT 0 /* No parent for the node */ + +# define MXML_DESCEND 1 /* Descend when finding/walking */ +# define MXML_NO_DESCEND 0 /* Don't descend when finding/walking */ +# define MXML_DESCEND_FIRST -1 /* Descend for first find */ + +# define MXML_WS_BEFORE_OPEN 0 /* Callback for before open tag */ +# define MXML_WS_AFTER_OPEN 1 /* Callback for after open tag */ +# define MXML_WS_BEFORE_CLOSE 2 /* Callback for before close tag */ +# define MXML_WS_AFTER_CLOSE 3 /* Callback for after close tag */ + +# define MXML_ADD_BEFORE 0 /* Add node before specified node */ +# define MXML_ADD_AFTER 1 /* Add node after specified node */ +# define MXML_ADD_TO_PARENT NULL /* Add node relative to parent */ + + +/* + * Data types... + */ + +typedef enum mxml_sax_event_e /**** SAX event type. ****/ +{ + MXML_SAX_CDATA, /* CDATA node */ + MXML_SAX_COMMENT, /* Comment node */ + MXML_SAX_DATA, /* Data node */ + MXML_SAX_DIRECTIVE, /* Processing directive node */ + MXML_SAX_ELEMENT_CLOSE, /* Element closed */ + MXML_SAX_ELEMENT_OPEN /* Element opened */ +} mxml_sax_event_t; + +typedef enum mxml_type_e /**** The XML node type. ****/ +{ + MXML_IGNORE = -1, /* Ignore/throw away node @since Mini-XML 2.3@ */ + MXML_ELEMENT, /* XML element with attributes */ + MXML_INTEGER, /* Integer value */ + MXML_OPAQUE, /* Opaque string */ + MXML_REAL, /* Real value */ + MXML_TEXT, /* Text fragment */ + MXML_CUSTOM /* Custom data @since Mini-XML 2.1@ */ +} mxml_type_t; + +typedef void (*mxml_custom_destroy_cb_t)(void *); + /**** Custom data destructor ****/ + +typedef void (*mxml_error_cb_t)(const char *); + /**** Error callback function ****/ + +typedef struct mxml_attr_s /**** An XML element attribute value. @private@ ****/ +{ + char *name; /* Attribute name */ + char *value; /* Attribute value */ +} mxml_attr_t; + +typedef struct mxml_element_s /**** An XML element value. @private@ ****/ +{ + char *name; /* Name of element */ + int num_attrs; /* Number of attributes */ + mxml_attr_t *attrs; /* Attributes */ +} mxml_element_t; + +typedef struct mxml_text_s /**** An XML text value. @private@ ****/ +{ + int whitespace; /* Leading whitespace? */ + char *string; /* Fragment string */ +} mxml_text_t; + +typedef struct mxml_custom_s /**** An XML custom value. @private@ ****/ +{ + void *data; /* Pointer to (allocated) custom data */ + mxml_custom_destroy_cb_t destroy; /* Pointer to destructor function */ +} mxml_custom_t; + +typedef union mxml_value_u /**** An XML node value. @private@ ****/ +{ + mxml_element_t element; /* Element */ + int integer; /* Integer number */ + char *opaque; /* Opaque string */ + double real; /* Real number */ + mxml_text_t text; /* Text fragment */ + mxml_custom_t custom; /* Custom data @since Mini-XML 2.1@ */ +} mxml_value_t; + +struct mxml_node_s /**** An XML node. @private@ ****/ +{ + mxml_type_t type; /* Node type */ + struct mxml_node_s *next; /* Next node under same parent */ + struct mxml_node_s *prev; /* Previous node under same parent */ + struct mxml_node_s *parent; /* Parent node */ + struct mxml_node_s *child; /* First child node */ + struct mxml_node_s *last_child; /* Last child node */ + mxml_value_t value; /* Node value */ + int ref_count; /* Use count */ + void *user_data; /* User data */ +}; + +typedef struct mxml_node_s mxml_node_t; /**** An XML node. ****/ + +struct mxml_index_s /**** An XML node index. @private@ ****/ +{ + char *attr; /* Attribute used for indexing or NULL */ + int num_nodes; /* Number of nodes in index */ + int alloc_nodes; /* Allocated nodes in index */ + int cur_node; /* Current node */ + mxml_node_t **nodes; /* Node array */ +}; + +typedef struct mxml_index_s mxml_index_t; + /**** An XML node index. ****/ + +typedef int (*mxml_custom_load_cb_t)(mxml_node_t *, const char *); + /**** Custom data load callback function ****/ + +typedef char *(*mxml_custom_save_cb_t)(mxml_node_t *); + /**** Custom data save callback function ****/ + +typedef int (*mxml_entity_cb_t)(const char *); + /**** Entity callback function */ + +typedef mxml_type_t (*mxml_load_cb_t)(mxml_node_t *); + /**** Load callback function ****/ + +typedef const char *(*mxml_save_cb_t)(mxml_node_t *, int); + /**** Save callback function ****/ + +typedef void (*mxml_sax_cb_t)(mxml_node_t *, mxml_sax_event_t, void *); + /**** SAX callback function ****/ + + +/* + * C++ support... + */ + +# ifdef __cplusplus +extern "C" { +# endif /* __cplusplus */ + +/* + * Prototypes... + */ + +extern void mxmlAdd(mxml_node_t *parent, int where, + mxml_node_t *child, mxml_node_t *node); +extern void mxmlDelete(mxml_node_t *node); +extern void mxmlElementDeleteAttr(mxml_node_t *node, + const char *name); +extern const char *mxmlElementGetAttr(mxml_node_t *node, const char *name); +extern void mxmlElementSetAttr(mxml_node_t *node, const char *name, + const char *value); +extern void mxmlElementSetAttrf(mxml_node_t *node, const char *name, + const char *format, ...) +# ifdef __GNUC__ +__attribute__ ((__format__ (__printf__, 3, 4))) +# endif /* __GNUC__ */ +; +extern int mxmlEntityAddCallback(mxml_entity_cb_t cb); +extern const char *mxmlEntityGetName(int val); +extern int mxmlEntityGetValue(const char *name); +extern void mxmlEntityRemoveCallback(mxml_entity_cb_t cb); +extern mxml_node_t *mxmlFindElement(mxml_node_t *node, mxml_node_t *top, + const char *name, const char *attr, + const char *value, int descend); +extern mxml_node_t *mxmlFindPath(mxml_node_t *node, const char *path); +extern const char *mxmlGetCDATA(mxml_node_t *node); +extern const void *mxmlGetCustom(mxml_node_t *node); +extern const char *mxmlGetElement(mxml_node_t *node); +extern mxml_node_t *mxmlGetFirstChild(mxml_node_t *node); +extern int mxmlGetInteger(mxml_node_t *node); +extern mxml_node_t *mxmlGetLastChild(mxml_node_t *node); +extern mxml_node_t *mxmlGetNextSibling(mxml_node_t *node); +extern const char *mxmlGetOpaque(mxml_node_t *node); +extern mxml_node_t *mxmlGetParent(mxml_node_t *node); +extern mxml_node_t *mxmlGetPrevSibling(mxml_node_t *node); +extern double mxmlGetReal(mxml_node_t *node); +extern int mxmlGetRefCount(mxml_node_t *node); +extern const char *mxmlGetText(mxml_node_t *node, int *whitespace); +extern mxml_type_t mxmlGetType(mxml_node_t *node); +extern void *mxmlGetUserData(mxml_node_t *node); +extern void mxmlIndexDelete(mxml_index_t *ind); +extern mxml_node_t *mxmlIndexEnum(mxml_index_t *ind); +extern mxml_node_t *mxmlIndexFind(mxml_index_t *ind, + const char *element, + const char *value); +extern int mxmlIndexGetCount(mxml_index_t *ind); +extern mxml_index_t *mxmlIndexNew(mxml_node_t *node, const char *element, + const char *attr); +extern mxml_node_t *mxmlIndexReset(mxml_index_t *ind); +extern mxml_node_t *mxmlLoadFd(mxml_node_t *top, int fd, + mxml_type_t (*cb)(mxml_node_t *)); +extern mxml_node_t *mxmlLoadFile(mxml_node_t *top, FILE *fp, + mxml_type_t (*cb)(mxml_node_t *)); +extern mxml_node_t *mxmlLoadString(mxml_node_t *top, const char *s, + mxml_type_t (*cb)(mxml_node_t *)); +extern mxml_node_t *mxmlNewCDATA(mxml_node_t *parent, const char *string); +extern mxml_node_t *mxmlNewCustom(mxml_node_t *parent, void *data, + mxml_custom_destroy_cb_t destroy); +extern mxml_node_t *mxmlNewElement(mxml_node_t *parent, const char *name); +extern mxml_node_t *mxmlNewInteger(mxml_node_t *parent, int integer); +extern mxml_node_t *mxmlNewOpaque(mxml_node_t *parent, const char *opaque); +extern mxml_node_t *mxmlNewReal(mxml_node_t *parent, double real); +extern mxml_node_t *mxmlNewText(mxml_node_t *parent, int whitespace, + const char *string); +extern mxml_node_t *mxmlNewTextf(mxml_node_t *parent, int whitespace, + const char *format, ...) +# ifdef __GNUC__ +__attribute__ ((__format__ (__printf__, 3, 4))) +# endif /* __GNUC__ */ +; +extern mxml_node_t *mxmlNewXML(const char *version); +extern int mxmlRelease(mxml_node_t *node); +extern void mxmlRemove(mxml_node_t *node); +extern int mxmlRetain(mxml_node_t *node); +extern char *mxmlSaveAllocString(mxml_node_t *node, + mxml_save_cb_t cb); +extern int mxmlSaveFd(mxml_node_t *node, int fd, + mxml_save_cb_t cb); +extern int mxmlSaveFile(mxml_node_t *node, FILE *fp, + mxml_save_cb_t cb); +extern int mxmlSaveString(mxml_node_t *node, char *buffer, + int bufsize, mxml_save_cb_t cb); +extern mxml_node_t *mxmlSAXLoadFd(mxml_node_t *top, int fd, + mxml_type_t (*cb)(mxml_node_t *), + mxml_sax_cb_t sax, void *sax_data); +extern mxml_node_t *mxmlSAXLoadFile(mxml_node_t *top, FILE *fp, + mxml_type_t (*cb)(mxml_node_t *), + mxml_sax_cb_t sax, void *sax_data); +extern mxml_node_t *mxmlSAXLoadString(mxml_node_t *top, const char *s, + mxml_type_t (*cb)(mxml_node_t *), + mxml_sax_cb_t sax, void *sax_data); +extern int mxmlSetCDATA(mxml_node_t *node, const char *data); +extern int mxmlSetCustom(mxml_node_t *node, void *data, + mxml_custom_destroy_cb_t destroy); +extern void mxmlSetCustomHandlers(mxml_custom_load_cb_t load, + mxml_custom_save_cb_t save); +extern int mxmlSetElement(mxml_node_t *node, const char *name); +extern void mxmlSetErrorCallback(mxml_error_cb_t cb); +extern int mxmlSetInteger(mxml_node_t *node, int integer); +extern int mxmlSetOpaque(mxml_node_t *node, const char *opaque); +extern int mxmlSetReal(mxml_node_t *node, double real); +extern int mxmlSetText(mxml_node_t *node, int whitespace, + const char *string); +extern int mxmlSetTextf(mxml_node_t *node, int whitespace, + const char *format, ...) +# ifdef __GNUC__ +__attribute__ ((__format__ (__printf__, 3, 4))) +# endif /* __GNUC__ */ +; +extern int mxmlSetUserData(mxml_node_t *node, void *data); +extern void mxmlSetWrapMargin(int column); +extern mxml_node_t *mxmlWalkNext(mxml_node_t *node, mxml_node_t *top, + int descend); +extern mxml_node_t *mxmlWalkPrev(mxml_node_t *node, mxml_node_t *top, + int descend); + + +/* + * Semi-private functions... + */ + +extern void mxml_error(const char *format, ...); +extern mxml_type_t mxml_ignore_cb(mxml_node_t *node); +extern mxml_type_t mxml_integer_cb(mxml_node_t *node); +extern mxml_type_t mxml_opaque_cb(mxml_node_t *node); +extern mxml_type_t mxml_real_cb(mxml_node_t *node); + + +/* + * C++ support... + */ + +# ifdef __cplusplus +} +# endif /* __cplusplus */ +#endif /* !_mxml_h_ */ + + +/* + * End of "$Id: mxml.h 427 2011-01-03 02:03:29Z mike $". + */ diff --git a/driver/Makefile b/driver/Makefile index 8824f8e..667637e 100644 --- a/driver/Makefile +++ b/driver/Makefile @@ -11,21 +11,10 @@ gator-y := gator_main.o \ gator_events_net.o \ gator_events_block.o \ gator_events_meminfo.o \ - gator_events_power.o \ gator_events_perf_pmu.o gator-y += gator_events_mmaped.o -ifeq ($(GATOR_WITH_MALI_SUPPORT),) -ifeq ($(GATOR_MALI_INCLUDE),) -GATOR_MALI_INCLUDE = $(abspath $(shell find -L . -name "mali_linux_trace.h" | sed -n -e '1s,\(.*\)/linux/mali_linux_trace.h$$,\1,p')) -endif -ifneq ($(GATOR_MALI_INCLUDE),) -GATOR_WITH_MALI_SUPPORT = MALI_400 # for now, assume all devices with Mali have the Mali-400 -EXTRA_CFLAGS += -I$(GATOR_MALI_INCLUDE) -endif -endif - ifneq ($(GATOR_WITH_MALI_SUPPORT),) ifeq ($(GATOR_WITH_MALI_SUPPORT),MALI_T6xx) gator-y += gator_events_mali_t6xx.o diff --git a/driver/gator.h b/driver/gator.h index a7a323c..6b96109 100644 --- a/driver/gator.h +++ b/driver/gator.h @@ -88,6 +88,7 @@ struct gator_interface { struct list_head list; }; +// gator_events_init is used as a search term in gator_events.sh #define gator_events_init(initfn) \ static inline int __gator_events_init_test(void) \ { return initfn(); } diff --git a/driver/gator_annotate.c b/driver/gator_annotate.c index 36a921c..b2288b3 100644 --- a/driver/gator_annotate.c +++ b/driver/gator_annotate.c @@ -15,65 +15,93 @@ #include #include -#define ANNOTATE_SIZE (16*1024) static DEFINE_SPINLOCK(annotate_lock); -static char *annotateBuf; -static char *annotateBuf0; -static char *annotateBuf1; -static int annotatePos; -static int annotateSel; static bool collect_annotations = false; -static ssize_t annotate_write(struct file *file, char const __user *buf, size_t count, loff_t *offset) +static int annotate_copy(struct file *file, char const __user *buf, size_t count) { - char tempBuffer[512]; - int remaining, size; - uint32_t tid; + int cpu = 0; + int write = per_cpu(gator_buffer_write, cpu)[ANNOTATE_BUF]; + + if (file == NULL) { + // copy from kernel + memcpy(&per_cpu(gator_buffer, cpu)[ANNOTATE_BUF][write], buf, count); + } else { + // copy from user space + if (copy_from_user(&per_cpu(gator_buffer, cpu)[ANNOTATE_BUF][write], buf, count) != 0) + return -1; + } + per_cpu(gator_buffer_write, cpu)[ANNOTATE_BUF] = (write + count) & gator_buffer_mask[ANNOTATE_BUF]; + + return 0; +} + +static ssize_t annotate_write(struct file *file, char const __user *buf, size_t count_orig, loff_t *offset) +{ + int tid, cpu, header_size, available, contiguous, length1, length2, size, count = count_orig & 0x7fffffff; if (*offset) return -EINVAL; - // determine size to capture - size = count < sizeof(tempBuffer) ? count : sizeof(tempBuffer); + if (!collect_annotations) { + return count_orig; + } + + cpu = 0; // Annotation only uses a single per-cpu buffer as the data must be in order to the engine - // note: copy may be for naught if remaining is zero, but better to do the copy outside of the spinlock if (file == NULL) { - // copy from kernel - memcpy(tempBuffer, buf, size); - - // set the thread id to the kernel thread, not the current thread - tid = -1; + tid = -1; // set the thread id to the kernel thread } else { - // copy from user space - if (copy_from_user(tempBuffer, buf, size) != 0) - return -EINVAL; tid = current->pid; } - // synchronize shared variables annotateBuf and annotatePos + // synchronize between cores spin_lock(&annotate_lock); - if (collect_annotations && annotateBuf) { - remaining = ANNOTATE_SIZE - annotatePos - 256; // pad for headers and release - size = size < remaining ? size : remaining; - if (size > 0) { - uint64_t time = gator_get_time(); - uint32_t cpuid = smp_processor_id(); - int pos = annotatePos; - pos += gator_write_packed_int(&annotateBuf[pos], tid); - pos += gator_write_packed_int64(&annotateBuf[pos], time); - pos += gator_write_packed_int(&annotateBuf[pos], cpuid); - pos += gator_write_packed_int(&annotateBuf[pos], size); - memcpy(&annotateBuf[pos], tempBuffer, size); - annotatePos = pos + size; - } - } - spin_unlock(&annotate_lock); + + // determine total size of the payload + header_size = MAXSIZE_PACK32 * 3 + MAXSIZE_PACK64; + available = buffer_bytes_available(cpu, ANNOTATE_BUF) - header_size; + size = count < available ? count : available; if (size <= 0) { - wake_up(&gator_buffer_wait); - return 0; + size = 0; + goto annotate_write_out; } + // synchronize shared variables annotateBuf and annotatePos + if (collect_annotations && per_cpu(gator_buffer, cpu)[ANNOTATE_BUF]) { + gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, smp_processor_id()); + gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, tid); + gator_buffer_write_packed_int64(cpu, ANNOTATE_BUF, gator_get_time()); + gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, size); + + // determine the sizes to capture, length1 + length2 will equal size + contiguous = contiguous_space_available(cpu, ANNOTATE_BUF); + if (size < contiguous) { + length1 = size; + length2 = 0; + } else { + length1 = contiguous; + length2 = size - contiguous; + } + + if (annotate_copy(file, buf, length1) != 0) { + size = -EINVAL; + goto annotate_write_out; + } + + if (length2 > 0 && annotate_copy(file, &buf[length1], length2) != 0) { + size = -EINVAL; + goto annotate_write_out; + } + + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, ANNOTATE_BUF); + } + +annotate_write_out: + spin_unlock(&annotate_lock); + // return the number of bytes written return size; } @@ -82,21 +110,18 @@ static ssize_t annotate_write(struct file *file, char const __user *buf, size_t static int annotate_release(struct inode *inode, struct file *file) { - int remaining = ANNOTATE_SIZE - annotatePos; - if (remaining < 16) { - return -EFAULT; - } + int cpu = 0; + // synchronize between cores spin_lock(&annotate_lock); - if (annotateBuf) { + + if (per_cpu(gator_buffer, cpu)[ANNOTATE_BUF] && buffer_check_space(cpu, ANNOTATE_BUF, MAXSIZE_PACK64 + 2 * MAXSIZE_PACK32)) { uint32_t tid = current->pid; - int pos = annotatePos; - pos += gator_write_packed_int(&annotateBuf[pos], tid); - pos += gator_write_packed_int64(&annotateBuf[pos], 0); // time - pos += gator_write_packed_int(&annotateBuf[pos], 0); // cpuid - pos += gator_write_packed_int(&annotateBuf[pos], 0); // size - annotatePos = pos; + gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, tid); + gator_buffer_write_packed_int64(cpu, ANNOTATE_BUF, 0); // time + gator_buffer_write_packed_int(cpu, ANNOTATE_BUF, 0); // size } + spin_unlock(&annotate_lock); return 0; @@ -109,25 +134,11 @@ static const struct file_operations annotate_fops = { static int gator_annotate_create_files(struct super_block *sb, struct dentry *root) { - annotateBuf = NULL; return gatorfs_create_file_perm(sb, root, "annotate", &annotate_fops, 0666); } -static int gator_annotate_init(void) -{ - annotateBuf0 = kmalloc(ANNOTATE_SIZE, GFP_KERNEL); - annotateBuf1 = kmalloc(ANNOTATE_SIZE, GFP_KERNEL); - if (!annotateBuf0 || !annotateBuf1) - return -1; - return 0; -} - static int gator_annotate_start(void) { - annotateSel = 0; - annotatePos = 1; - annotateBuf = annotateBuf0; - annotateBuf[0] = FRAME_ANNOTATE; collect_annotations = true; return 0; } @@ -136,46 +147,3 @@ static void gator_annotate_stop(void) { collect_annotations = false; } - -static void gator_annotate_shutdown(void) -{ - spin_lock(&annotate_lock); - annotateBuf = NULL; - spin_unlock(&annotate_lock); -} - -static void gator_annotate_exit(void) -{ - spin_lock(&annotate_lock); - kfree(annotateBuf0); - kfree(annotateBuf1); - annotateBuf = annotateBuf0 = annotateBuf1 = NULL; - spin_unlock(&annotate_lock); -} - -static int gator_annotate_ready(void) -{ - return annotatePos > 1 && annotateBuf; -} - -static int gator_annotate_read(char **buffer) -{ - int len; - - if (!gator_annotate_ready()) - return 0; - - annotateSel = !annotateSel; - - if (buffer) - *buffer = annotateBuf; - - spin_lock(&annotate_lock); - len = annotatePos; - annotateBuf = annotateSel ? annotateBuf1 : annotateBuf0; - annotateBuf[0] = FRAME_ANNOTATE; - annotatePos = 1; - spin_unlock(&annotate_lock); - - return len; -} diff --git a/driver/gator_backtrace.c b/driver/gator_backtrace.c index 26503ef..50783d6 100644 --- a/driver/gator_backtrace.c +++ b/driver/gator_backtrace.c @@ -81,8 +81,7 @@ static int report_trace(struct stackframe *frame, void *d) cookie = get_cookie(cpu, per_cpu(backtrace_buffer, cpu), current, NULL, mod, true); addr = addr - (unsigned long)mod->module_core; } - gator_buffer_write_packed_int(cpu, per_cpu(backtrace_buffer, cpu), addr & ~1); - gator_buffer_write_packed_int(cpu, per_cpu(backtrace_buffer, cpu), cookie); + marshal_backtrace(addr & ~1, cookie); (*depth)--; } @@ -110,7 +109,6 @@ static void kernel_backtrace(int cpu, int buftype, struct pt_regs * const regs) per_cpu(backtrace_buffer, cpu) = buftype; walk_stackframe(&frame, report_trace, &depth); #else - gator_buffer_write_packed_int(cpu, buftype, PC_REG & ~1); - gator_buffer_write_packed_int(cpu, buftype, NO_COOKIE); + marshal_backtrace(PC_REG & ~1, NO_COOKIE); #endif } diff --git a/driver/gator_cookies.c b/driver/gator_cookies.c index 1beb34f..7b50916 100644 --- a/driver/gator_cookies.c +++ b/driver/gator_cookies.c @@ -129,7 +129,7 @@ static void wq_cookie_handler(struct work_struct *unused) while (per_cpu(translate_buffer_read, cpu) != commit) { task = (struct task_struct *)translate_buffer_read_int(cpu); vma = (struct vm_area_struct *)translate_buffer_read_int(cpu); - cookie = get_cookie(cpu, TIMER_BUF, task, vma, NULL, false); + cookie = get_cookie(cpu, BACKTRACE_BUF, task, vma, NULL, false); } } @@ -251,13 +251,10 @@ static inline uint32_t get_cookie(int cpu, int buftype, struct task_struct *task local_irq_save(flags); cookie = INVALID_COOKIE; - if (buffer_check_space(cpu, buftype, strlen(text) + 2 * MAXSIZE_PACK32)) { + if (marshal_cookie_header(text)) { cookie = per_cpu(cookie_next_key, cpu) += nr_cpu_ids; cookiemap_add(key, cookie); - - gator_buffer_write_packed_int(cpu, buftype, MESSAGE_COOKIE); - gator_buffer_write_packed_int(cpu, buftype, cookie); - gator_buffer_write_string(cpu, buftype, text); + marshal_cookie(cookie, text); } local_irq_restore(flags); @@ -271,6 +268,7 @@ static int get_exec_cookie(int cpu, int buftype, struct task_struct *task) struct mm_struct *mm = task->mm; struct vm_area_struct *vma; + // kernel threads have no address space if (!mm) return cookie; diff --git a/driver/gator_ebs.c b/driver/gator_ebs.c index 8c2997c..1208d69 100644 --- a/driver/gator_ebs.c +++ b/driver/gator_ebs.c @@ -14,6 +14,8 @@ #include +static DEFINE_MUTEX(perf_mutex); + extern int pmnc_counters; extern int ccnt; extern unsigned long pmnc_enabled[]; @@ -32,59 +34,16 @@ static void ebs_overflow_handler(struct perf_event *event, int unused, struct pe static void ebs_overflow_handler(struct perf_event *event, struct perf_sample_data *data, struct pt_regs *regs) #endif { - unsigned int value, delta, cpu = smp_processor_id(), buftype = EVENT_BUF; + int cpu = smp_processor_id(); if (event != per_cpu(pevent, cpu)) return; - if (buffer_check_space(cpu, buftype, 5 * MAXSIZE_PACK32 + MAXSIZE_PACK64)) { - value = local64_read(&event->count); - delta = value - per_cpu(prev_value, cpu); - per_cpu(prev_value, cpu) = value; - - // Counters header - gator_buffer_write_packed_int(cpu, buftype, MESSAGE_COUNTERS); // type - gator_buffer_write_packed_int64(cpu, buftype, gator_get_time()); // time - - // Output counter - gator_buffer_write_packed_int(cpu, buftype, 2); // length - gator_buffer_write_packed_int(cpu, buftype, per_cpu(key, cpu)); // key - gator_buffer_write_packed_int(cpu, buftype, delta); // delta - - // End Counters, length of zero - gator_buffer_write_packed_int(cpu, buftype, 0); - } - // Output backtrace - if (buffer_check_space(cpu, buftype, gator_backtrace_depth * 2 * MAXSIZE_PACK32)) - gator_add_sample(cpu, buftype, regs); + gator_add_sample(cpu, BACKTRACE_BUF, regs); - // Check and commit; commit is set to occur once buffer is 3/4 full - buffer_check(cpu, buftype); -} - -static void gator_event_sampling_online(void) -{ - int cpu = smp_processor_id(), buftype = EVENT_BUF; - - // read the counter and toss the invalid data, return zero instead - struct perf_event * ev = per_cpu(pevent, cpu); - if (ev != NULL && ev->state == PERF_EVENT_STATE_ACTIVE) { - ev->pmu->read(ev); - per_cpu(prev_value, cpu) = local64_read(&ev->count); - - // Counters header - gator_buffer_write_packed_int(cpu, buftype, MESSAGE_COUNTERS); // type - gator_buffer_write_packed_int64(cpu, buftype, gator_get_time()); // time - - // Output counter - gator_buffer_write_packed_int(cpu, buftype, 2); // length - gator_buffer_write_packed_int(cpu, buftype, per_cpu(key, cpu)); // key - gator_buffer_write_packed_int(cpu, buftype, 0); // delta - zero for initialization - - // End Counters, length of zero - gator_buffer_write_packed_int(cpu, buftype, 0); - } + // Collect counters + collect_counters(); } static void gator_event_sampling_online_dispatch(int cpu) @@ -117,10 +76,18 @@ static void gator_event_sampling_online_dispatch(int cpu) static void gator_event_sampling_offline_dispatch(int cpu) { + struct perf_event * pe = NULL; + + mutex_lock(&perf_mutex); if (per_cpu(pevent, cpu)) { - perf_event_release_kernel(per_cpu(pevent, cpu)); + pe = per_cpu(pevent, cpu); per_cpu(pevent, cpu) = NULL; } + mutex_unlock(&perf_mutex); + + if (pe) { + perf_event_release_kernel(pe); + } } static int gator_event_sampling_start(void) @@ -184,7 +151,6 @@ static void gator_event_sampling_stop(void) } #else -static void gator_event_sampling_online(void) {} static void gator_event_sampling_online_dispatch(int cpu) {} static void gator_event_sampling_offline_dispatch(int cpu) {} static int gator_event_sampling_start(void) {return 0;} diff --git a/driver/gator_events_armv6.c b/driver/gator_events_armv6.c index ef51898..5f989ba 100644 --- a/driver/gator_events_armv6.c +++ b/driver/gator_events_armv6.c @@ -33,7 +33,6 @@ static unsigned long pmnc_enabled[CNTMAX]; static unsigned long pmnc_event[CNTMAX]; static unsigned long pmnc_key[CNTMAX]; -static DEFINE_PER_CPU(int[CNTMAX], perfPrev); static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt); static inline void armv6_pmnc_write(u32 val) @@ -111,8 +110,6 @@ static int gator_events_armv6_online(int** buffer) for (pmnc = 0, cnt = PMN0; cnt <= CCNT; cnt++) { unsigned long event; - per_cpu(perfPrev, cpu)[cnt] = 0; - if (!pmnc_enabled[cnt]) continue; @@ -171,6 +168,11 @@ static int gator_events_armv6_read(int **buffer) int cnt, len = 0; int cpu = smp_processor_id(); + // a context switch may occur before the online hotplug event, thus need to check that the pmu is enabled + if (!(armv6_pmnc_read() & PMCR_E)) { + return 0; + } + for (cnt = PMN0; cnt <= CCNT; cnt++) { if (pmnc_enabled[cnt]) { u32 value = 0; @@ -186,11 +188,9 @@ static int gator_events_armv6_read(int **buffer) break; } armv6_pmnc_reset_counter(cnt); - if (value != per_cpu(perfPrev, cpu)[cnt]) { - per_cpu(perfPrev, cpu)[cnt] = value; - per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; - per_cpu(perfCnt, cpu)[len++] = value; - } + + per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; + per_cpu(perfCnt, cpu)[len++] = value; } } diff --git a/driver/gator_events_armv7.c b/driver/gator_events_armv7.c index cdf450f..590421d 100644 --- a/driver/gator_events_armv7.c +++ b/driver/gator_events_armv7.c @@ -38,7 +38,6 @@ static unsigned long pmnc_enabled[CNTMAX]; static unsigned long pmnc_event[CNTMAX]; static unsigned long pmnc_key[CNTMAX]; -static DEFINE_PER_CPU(int[CNTMAX], perfPrev); static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt); inline void armv7_pmnc_write(u32 val) @@ -177,8 +176,6 @@ static int gator_events_armv7_online(int** buffer) for (cnt = CCNT; cnt < CNTMAX; cnt++) { unsigned long event; - per_cpu(perfPrev, cpu)[cnt] = 0; - if (!pmnc_enabled[cnt]) continue; @@ -240,6 +237,11 @@ static int gator_events_armv7_read(int **buffer) int cnt, len = 0; int cpu = smp_processor_id(); + // a context switch may occur before the online hotplug event, thus need to check that the pmu is enabled + if (!(armv7_pmnc_read() & PMNC_E)) { + return 0; + } + for (cnt = 0; cnt < pmnc_counters; cnt++) { if (pmnc_enabled[cnt]) { int value; @@ -248,11 +250,8 @@ static int gator_events_armv7_read(int **buffer) } else { value = armv7_cntn_read(cnt, 0); } - if (value != per_cpu(perfPrev, cpu)[cnt]) { - per_cpu(perfPrev, cpu)[cnt] = value; - per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; - per_cpu(perfCnt, cpu)[len++] = value; - } + per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; + per_cpu(perfCnt, cpu)[len++] = value; } } diff --git a/driver/gator_events_block.c b/driver/gator_events_block.c index f1bbbc8..a8b8114 100644 --- a/driver/gator_events_block.c +++ b/driver/gator_events_block.c @@ -26,7 +26,7 @@ static ulong block_rq_rd_enabled; static ulong block_rq_wr_key; static ulong block_rq_rd_key; static DEFINE_PER_CPU(int[BLOCK_TOTAL], blockCnt); -static DEFINE_PER_CPU(int[BLOCK_TOTAL * 2], blockGet); +static DEFINE_PER_CPU(int[BLOCK_TOTAL * 4], blockGet); static DEFINE_PER_CPU(bool, new_data_avail); GATOR_DEFINE_PROBE(block_rq_complete, TP_PROTO(struct request_queue *q, struct request *rq)) @@ -129,6 +129,8 @@ static int gator_events_block_read(int **buffer) per_cpu(blockCnt, cpu)[BLOCK_RQ_WR] = 0; local_irq_restore(flags); per_cpu(blockGet, cpu)[len++] = block_rq_wr_key; + per_cpu(blockGet, cpu)[len++] = 0; // indicates to Streamline that value bytes were written now, not since the last message + per_cpu(blockGet, cpu)[len++] = block_rq_wr_key; per_cpu(blockGet, cpu)[len++] = value; data += value; } @@ -138,6 +140,8 @@ static int gator_events_block_read(int **buffer) per_cpu(blockCnt, cpu)[BLOCK_RQ_RD] = 0; local_irq_restore(flags); per_cpu(blockGet, cpu)[len++] = block_rq_rd_key; + per_cpu(blockGet, cpu)[len++] = 0; // indicates to Streamline that value bytes were read now, not since the last message + per_cpu(blockGet, cpu)[len++] = block_rq_rd_key; per_cpu(blockGet, cpu)[len++] = value; data += value; } diff --git a/driver/gator_events_irq.c b/driver/gator_events_irq.c index 59461b9..435bc86 100644 --- a/driver/gator_events_irq.c +++ b/driver/gator_events_irq.c @@ -19,7 +19,6 @@ static ulong softirq_enabled; static ulong hardirq_key; static ulong softirq_key; static DEFINE_PER_CPU(int[TOTALIRQ], irqCnt); -static DEFINE_PER_CPU(int[TOTALIRQ], irqPrev); static DEFINE_PER_CPU(int[TOTALIRQ * 2], irqGet); GATOR_DEFINE_PROBE(irq_handler_exit, TP_PROTO(int irq, @@ -82,7 +81,6 @@ static int gator_events_irq_online(int** buffer) local_irq_save(flags); per_cpu(irqCnt, cpu)[HARDIRQ] = 0; local_irq_restore(flags); - per_cpu(irqPrev, cpu)[HARDIRQ] = 0; per_cpu(irqGet, cpu)[len++] = hardirq_key; per_cpu(irqGet, cpu)[len++] = 0; } @@ -91,7 +89,6 @@ static int gator_events_irq_online(int** buffer) local_irq_save(flags); per_cpu(irqCnt, cpu)[SOFTIRQ] = 0; local_irq_restore(flags); - per_cpu(irqPrev, cpu)[SOFTIRQ] = 0; per_cpu(irqGet, cpu)[len++] = softirq_key; per_cpu(irqGet, cpu)[len++] = 0; } @@ -149,11 +146,9 @@ static int gator_events_irq_read(int **buffer) value = per_cpu(irqCnt, cpu)[HARDIRQ]; per_cpu(irqCnt, cpu)[HARDIRQ] = 0; local_irq_restore(flags); - if (value != per_cpu(irqPrev, cpu)[HARDIRQ]) { - per_cpu(irqPrev, cpu)[HARDIRQ] = value; - per_cpu(irqGet, cpu)[len++] = hardirq_key; - per_cpu(irqGet, cpu)[len++] = value; - } + + per_cpu(irqGet, cpu)[len++] = hardirq_key; + per_cpu(irqGet, cpu)[len++] = value; } if (softirq_enabled) { @@ -161,11 +156,9 @@ static int gator_events_irq_read(int **buffer) value = per_cpu(irqCnt, cpu)[SOFTIRQ]; per_cpu(irqCnt, cpu)[SOFTIRQ] = 0; local_irq_restore(flags); - if (value != per_cpu(irqPrev, cpu)[SOFTIRQ]) { - per_cpu(irqPrev, cpu)[SOFTIRQ] = value; - per_cpu(irqGet, cpu)[len++] = softirq_key; - per_cpu(irqGet, cpu)[len++] = value; - } + + per_cpu(irqGet, cpu)[len++] = softirq_key; + per_cpu(irqGet, cpu)[len++] = value; } if (buffer) diff --git a/driver/gator_events_mali.c b/driver/gator_events_mali.c old mode 100644 new mode 100755 index 31e8f0d..21a6324 --- a/driver/gator_events_mali.c +++ b/driver/gator_events_mali.c @@ -14,17 +14,41 @@ #include "linux/mali_linux_trace.h" -#define ACTIVITY_START 1 -#define ACTIVITY_STOP 2 +/* + * There are (currently) three different variants of the comms between gator and Mali: + * 1 (deprecated): No software counter support + * 2 (deprecated): Tracepoint called for each separate s/w counter value as it appears + * 3 (default): Single tracepoint for all s/w counters in a bundle. + * Interface style 3 is the default if no other is specified. 1 and 2 will be eliminated when + * existing Mali DDKs are upgraded. + */ -#ifndef MALI_SUPPORT -#error MALI_SUPPORT not defined! +#if !defined(GATOR_MALI_INTERFACE_STYLE) +#define GATOR_MALI_INTERFACE_STYLE (3) #endif -#define MALI_200 0x0a07 -#define MALI_300 0x0b06 //This is not actually true; Mali-300 is also 0x0b07 -#define MALI_400 0x0b07 -#define MALI_T6xx 0x0056 +#define MALI_200 (0x0a07) +#define MALI_300 (0x0b06) //This is not actually true; Mali-300 is also 0x0b07 +#define MALI_400 (0x0b07) +#define MALI_T6xx (0x0056) + +/* + * List of possible actions allowing DDK to be controlled by Streamline. + * The following numbers are used by DDK to control the frame buffer dumping. + */ +#define FBDUMP_CONTROL_ENABLE (1) +#define FBDUMP_CONTROL_RATE (2) +#define SW_EVENTS_ENABLE (3) +#define FBDUMP_CONTROL_RESIZE_FACTOR (4) + +/* + * Check that the MALI_SUPPORT define is set to one of the allowable device codes. + */ +#if !defined(MALI_SUPPORT) +#error MALI_SUPPORT not defined! +#elif (MALI_SUPPORT != MALI_200) && (MALI_SUPPORT != MALI_300) && (MALI_SUPPORT != MALI_400) && (MALI_SUPPORT != MALI_T6xx) +#error MALI_SUPPORT set to an invalid device code +#endif static const char *mali_name; @@ -226,12 +250,31 @@ GATOR_DEFINE_PROBE(mali_hw_counter, TP_PROTO(unsigned int event_id, unsigned int } } +#if GATOR_MALI_INTERFACE_STYLE == 2 GATOR_DEFINE_PROBE(mali_sw_counter, TP_PROTO(unsigned int event_id, signed long long value)) { if (is_sw_counter(event_id)) { counter_data[event_id] = scale_sw_counter_value(event_id, value); } } +#endif /* GATOR_MALI_INTERFACE_STYLE == 2 */ + + +#if GATOR_MALI_INTERFACE_STYLE == 3 +GATOR_DEFINE_PROBE(mali_sw_counters, TP_PROTO(pid_t pid, pid_t tid, void * surface_id, unsigned int * counters)) +{ + u32 i; + + /* Copy over the values for those counters which are enabled. */ + for(i=FIRST_SW_COUNTER; i <= LAST_SW_COUNTER; i++) + { + if(counter_enabled[i]) + { + counter_data[i] = (u32)(counters[i - FIRST_SW_COUNTER]); + } + } +} +#endif /* GATOR_MALI_INTERFACE_STYLE == 3 */ //TODO need to work out how many fp units we have u32 gator_mali_get_n_fp(void) { @@ -363,10 +406,30 @@ int gator_events_mali_create_files(struct super_block *sb, struct dentry *root) //TODO void _mali_profiling_set_event(unsigned int, unsigned int); void _mali_osk_fb_control_set(unsigned int, unsigned int); +void _mali_profiling_control(unsigned int, unsigned int); void _mali_profiling_get_counters(unsigned int*, unsigned int*, unsigned int*, unsigned int*); void (*_mali_profiling_get_counters_function_pointer)(unsigned int*, unsigned int*, unsigned int*, unsigned int*); +/* + * Examine list of software counters and determine if any one is enabled. + * Returns 1 if any counter is enabled, 0 if none is. + */ +static int is_any_sw_counter_enabled(void) +{ + unsigned int i; + + for (i = FIRST_SW_COUNTER; i <= LAST_SW_COUNTER; i++) + { + if (counter_enabled[i]) + { + return 1; /* At least one counter is enabled */ + } + } + + return 0; /* No s/w counters enabled */ +} + static void mali_counter_initialize(void) { /* If a Mali driver is present and exporting the appropriate symbol @@ -375,6 +438,7 @@ static void mali_counter_initialize(void) */ void (*set_hw_event)(unsigned int, unsigned int); void (*set_fb_event)(unsigned int, unsigned int); + void (*mali_control)(unsigned int, unsigned int); set_hw_event = symbol_get(_mali_profiling_set_event); @@ -408,6 +472,27 @@ static void mali_counter_initialize(void) printk("gator: mali online _mali_osk_fb_control_set symbol not found\n"); } + /* Generic control interface for Mali DDK. */ + mali_control = symbol_get(_mali_profiling_control); + if (mali_control) { + /* The event attribute in the XML file keeps the actual frame rate. */ + unsigned int rate = counter_event[COUNTER_FILMSTRIP] & 0xff; + unsigned int resize_factor = (counter_event[COUNTER_FILMSTRIP] >> 8) & 0xff; + + pr_debug("gator: mali online _mali_profiling_control symbol @ %p\n", mali_control); + + mali_control(SW_EVENTS_ENABLE, (is_any_sw_counter_enabled()?1:0)); + mali_control(FBDUMP_CONTROL_ENABLE, (counter_enabled[COUNTER_FILMSTRIP]?1:0)); + mali_control(FBDUMP_CONTROL_RATE, rate); + mali_control(FBDUMP_CONTROL_RESIZE_FACTOR, resize_factor); + + pr_debug("gator: sent mali_control enabled=%d, rate=%d\n", (counter_enabled[COUNTER_FILMSTRIP]?1:0), rate); + + symbol_put(_mali_profiling_control); + } else { + printk("gator: mali online _mali_profiling_control symbol not found\n"); + } + _mali_profiling_get_counters_function_pointer = symbol_get(_mali_profiling_get_counters); if (_mali_profiling_get_counters_function_pointer){ pr_debug("gator: mali online _mali_profiling_get_counters symbol @ %p\n", _mali_profiling_get_counters_function_pointer); @@ -417,13 +502,13 @@ static void mali_counter_initialize(void) else{ pr_debug("gator WARNING: mali _mali_profiling_get_counters symbol not defined"); } - } static void mali_counter_deinitialize(void) { void (*set_hw_event)(unsigned int, unsigned int); void (*set_fb_event)(unsigned int, unsigned int); + void (*mali_control)(unsigned int, unsigned int); set_hw_event = symbol_get(_mali_profiling_set_event); @@ -452,6 +537,22 @@ static void mali_counter_deinitialize(void) printk("gator: mali offline _mali_osk_fb_control_set symbol not found\n"); } + /* Generic control interface for Mali DDK. */ + mali_control = symbol_get(_mali_profiling_control); + + if (mali_control) { + pr_debug("gator: mali offline _mali_profiling_control symbol @ %p\n", set_fb_event); + + /* Reset the DDK state - disable counter collection */ + mali_control(SW_EVENTS_ENABLE, 0); + + mali_control(FBDUMP_CONTROL_ENABLE, 0); + + symbol_put(_mali_profiling_control); + } else { + printk("gator: mali offline _mali_profiling_control symbol not found\n"); + } + if (_mali_profiling_get_counters_function_pointer){ symbol_put(_mali_profiling_get_counters); } @@ -465,10 +566,23 @@ static int gator_events_mali_start(void) { return -1; } +#if GATOR_MALI_INTERFACE_STYLE == 1 + /* None. */ +#elif GATOR_MALI_INTERFACE_STYLE == 2 + /* For patched Mali driver. */ if (GATOR_REGISTER_TRACE(mali_sw_counter)) { printk("gator: mali_sw_counter tracepoint failed to activate\n"); return -1; } +#elif GATOR_MALI_INTERFACE_STYLE == 3 +/* For Mali drivers with built-in support. */ + if (GATOR_REGISTER_TRACE(mali_sw_counters)) { + printk("gator: mali_sw_counters tracepoint failed to activate\n"); + return -1; + } +#else +#error Unknown GATOR_MALI_INTERFACE_STYLE option. +#endif trace_registered = 1; @@ -483,8 +597,19 @@ static void gator_events_mali_stop(void) { if (trace_registered) { GATOR_UNREGISTER_TRACE(mali_hw_counter); - GATOR_UNREGISTER_TRACE(mali_sw_counter); - + +#if GATOR_MALI_INTERFACE_STYLE == 1 + /* None. */ +#elif GATOR_MALI_INTERFACE_STYLE == 2 + /* For patched Mali driver. */ + GATOR_UNREGISTER_TRACE(mali_sw_counter); +#elif GATOR_MALI_INTERFACE_STYLE == 3 + /* For Mali drivers with built-in support. */ + GATOR_UNREGISTER_TRACE(mali_sw_counters); +#else +#error Unknown GATOR_MALI_INTERFACE_STYLE option. +#endif + pr_debug("gator: mali timeline tracepoint deactivated\n"); trace_registered = 0; @@ -538,22 +663,10 @@ static int gator_events_mali_read(int **buffer) { // Process other (non-timeline) counters. for (cnt = COUNTER_VP_C0; cnt <= LAST_SW_COUNTER; cnt++) { if (counter_enabled[cnt]) { - u32 value = 0; - - // Determine the current value of the counter. - if( counter_address[cnt] != NULL && 0 ) { // Never true! - value = *counter_address[cnt]; - } else if (counter_data[cnt]!=0) { - value = counter_data[cnt]; - counter_data[cnt] = 0; - } + counter_dump[len++] = counter_key[cnt]; + counter_dump[len++] = counter_data[cnt]; - // Send the counter value only if it differs from last time. - if (value != counter_prev[cnt]) { - counter_prev[cnt] = value; - counter_dump[len++] = counter_key[cnt]; - counter_dump[len++] = value; - } + counter_data[cnt] = 0; } } diff --git a/driver/gator_events_meminfo.c b/driver/gator_events_meminfo.c index c763634..ad552ef 100644 --- a/driver/gator_events_meminfo.c +++ b/driver/gator_events_meminfo.c @@ -10,6 +10,7 @@ #include "gator.h" #include #include +#include #define MEMINFO_MEMFREE 0 #define MEMINFO_MEMUSED 1 @@ -145,7 +146,7 @@ static void gator_events_meminfo_stop(void) } } -// Must be run in a work queue as the kernel function si_meminfo() can sleep +// Must be run in process context (work queue) as the kernel function si_meminfo() can sleep static void wq_sched_handler(struct work_struct *wsptr) { struct sysinfo info; @@ -189,7 +190,11 @@ static int gator_events_meminfo_read(long long **buffer) if (last_mem_event != mem_event) { last_mem_event = mem_event; - schedule_work(&work); + if (in_interrupt()) { + schedule_work(&work); + } else { + wq_sched_handler(NULL); + } } if (!new_data_avail) diff --git a/driver/gator_events_mmaped.c b/driver/gator_events_mmaped.c index cbb22b1..f81c402 100644 --- a/driver/gator_events_mmaped.c +++ b/driver/gator_events_mmaped.c @@ -14,11 +14,12 @@ * * * + * * * - * - * - * + * + * + * * */ @@ -42,6 +43,10 @@ static int mmaped_buffer[MMAPED_COUNTERS_NUM * 2]; static void __iomem *mmaped_base; #endif +#ifndef TODO +static s64 prev_time; +#endif + /* Adds mmaped_cntX directories and enabled, event, and key files to /dev/gator/events */ static int gator_events_mmaped_create_files(struct super_block *sb, struct dentry *root) @@ -77,6 +82,12 @@ static int gator_events_mmaped_start(void) writel(ENABLED, COUNTERS_CONTROL_OFFSET); #endif +#ifndef TODO + struct timespec ts; + getnstimeofday(&ts); + prev_time = timespec_to_ns(&ts); +#endif + return 0; } @@ -90,50 +101,60 @@ static void gator_events_mmaped_stop(void) #ifndef TODO /* This function "simulates" counters, generating values of fancy * functions like sine or triangle... */ -static int mmaped_simulate(int counter) +static int mmaped_simulate(int counter, int delta_in_us) { int result = 0; switch (counter) { case 0: /* sort-of-sine */ { - static int t; + static int t = 0; int x; - if (t % 1024 < 512) - x = 512 - (t % 512); + t += delta_in_us; + if (t > 2048000) + t = 0; + + if (t % 1024000 < 512000) + x = 512000 - (t % 512000); else - x = t % 512; + x = t % 512000; - result = 32 * x / 512; + result = 32 * x / 512000; result = result * result; - if (t < 1024) + if (t < 1024000) result = 1922 - result; - - t = (t + 1) % 2048; } break; case 1: /* triangle */ { static int v, d = 1; - v += d; - if (v % 2000 == 0) - d = -d; + v = v + d * delta_in_us; + if (v < 0) { + v = 0; + d = 1; + } else if (v > 1000000) { + v = 1000000; + d = -1; + } result = v; } break; case 2: /* PWM signal */ { - static int t, dc; - - t = (t + 1) % 2000; - if (t % 100) - dc = (dc + 200) % 2000; + static int t, dc, x; + + t += delta_in_us; + if (t > 1000000) + t = 0; + if (x / 1000000 != (x + delta_in_us) / 1000000) + dc = (dc + 100000) % 1000000; + x += delta_in_us; - result = t < dc ? 0 : 2000; + result = t < dc ? 0 : 10; } break; } @@ -146,11 +167,23 @@ static int gator_events_mmaped_read(int **buffer) { int i; int len = 0; +#ifndef TODO + int delta_in_us; + struct timespec ts; + s64 time; +#endif /* System wide counters - read from one core only */ if (smp_processor_id()) return 0; +#ifndef TODO + getnstimeofday(&ts); + time = timespec_to_ns(&ts); + delta_in_us = (int)(time - prev_time) / 1000; + prev_time = time; +#endif + for (i = 0; i < MMAPED_COUNTERS_NUM; i++) { if (mmaped_counters[i].enabled) { mmaped_buffer[len++] = mmaped_counters[i].key; @@ -159,7 +192,7 @@ static int gator_events_mmaped_read(int **buffer) COUNTERS_VALUE_OFFSET[i]); #else mmaped_buffer[len++] = mmaped_simulate( - mmaped_counters[i].event); + mmaped_counters[i].event, delta_in_us); #endif } } diff --git a/driver/gator_events_net.c b/driver/gator_events_net.c index ef1623b..9298905 100644 --- a/driver/gator_events_net.c +++ b/driver/gator_events_net.c @@ -9,6 +9,7 @@ #include "gator.h" #include +#include #define NETRX 0 #define NETTX 1 @@ -20,7 +21,7 @@ static ulong netrx_key; static ulong nettx_key; static int rx_total, tx_total; static ulong netPrev[TOTALNET]; -static int netGet[TOTALNET * 2]; +static int netGet[TOTALNET * 4]; static void get_network_stats(struct work_struct *wsptr) { int rx = 0, tx = 0; @@ -102,19 +103,31 @@ static int gator_events_net_read(int **buffer) if (smp_processor_id() != 0) return 0; - schedule_work(&wq_get_stats); + if (!netrx_enabled && !nettx_enabled) + return 0; + + if (in_interrupt()){ + schedule_work(&wq_get_stats); + } else { + get_network_stats(NULL); + } + calculate_delta(&rx_delta, &tx_delta); len = 0; if (netrx_enabled && last_rx_delta != rx_delta) { last_rx_delta = rx_delta; netGet[len++] = netrx_key; + netGet[len++] = 0; // indicates to Streamline that rx_delta bytes were transmitted now, not since the last message + netGet[len++] = netrx_key; netGet[len++] = rx_delta; } if (nettx_enabled && last_tx_delta != tx_delta) { last_tx_delta = tx_delta; netGet[len++] = nettx_key; + netGet[len++] = 0; // indicates to Streamline that tx_delta bytes were transmitted now, not since the last message + netGet[len++] = nettx_key; netGet[len++] = tx_delta; } diff --git a/driver/gator_events_perf_pmu.c b/driver/gator_events_perf_pmu.c old mode 100644 new mode 100755 index 322ebc4..da76a9c --- a/driver/gator_events_perf_pmu.c +++ b/driver/gator_events_perf_pmu.c @@ -19,6 +19,8 @@ int ccnt = 0; #define CNTMAX (6+1) +static DEFINE_MUTEX(perf_mutex); + unsigned long pmnc_enabled[CNTMAX]; unsigned long pmnc_event[CNTMAX]; unsigned long pmnc_count[CNTMAX]; @@ -122,12 +124,20 @@ static void gator_events_perf_pmu_online_dispatch(int cpu) static void gator_events_perf_pmu_offline_dispatch(int cpu) { int cnt; + struct perf_event * pe; for (cnt = 0; cnt < pmnc_counters; cnt++) { - if (per_cpu(pevent, cpu)[cnt] != NULL) { - perf_event_release_kernel(per_cpu(pevent, cpu)[cnt]); + pe = NULL; + mutex_lock(&perf_mutex); + if (per_cpu(pevent, cpu)[cnt]) { + pe = per_cpu(pevent, cpu)[cnt]; per_cpu(pevent, cpu)[cnt] = NULL; } + mutex_unlock(&perf_mutex); + + if (pe) { + perf_event_release_kernel(pe); + } } } @@ -139,7 +149,7 @@ static int gator_events_perf_pmu_start(void) for_each_present_cpu(cpu) { for (cnt = 0; cnt < pmnc_counters; cnt++) { per_cpu(pevent, cpu)[cnt] = NULL; - if (!pmnc_enabled[cnt] || pmnc_count[cnt] > 0) // Skip disabled counters and EBS counters + if (!pmnc_enabled[cnt]) // Skip disabled counters continue; per_cpu(perfPrev, cpu)[cnt] = 0; diff --git a/driver/gator_events_power.c b/driver/gator_events_power.c deleted file mode 100644 index a0ae684..0000000 --- a/driver/gator_events_power.c +++ /dev/null @@ -1,178 +0,0 @@ -/** - * Copyright (C) ARM Limited 2011-2012. 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 "gator.h" -#include -#include -#include - -// cpu_frequency and cpu_idle trace points were introduced in Linux kernel v2.6.38 -// the now deprecated power_frequency trace point was available prior to 2.6.38, but only for x86 -// CPU Idle is currently disabled in the .xml -#if GATOR_CPU_FREQ_SUPPORT -enum { - POWER_CPU_FREQ, - POWER_CPU_IDLE, - POWER_TOTAL -}; - -static ulong power_cpu_enabled[POWER_TOTAL]; -static ulong power_cpu_key[POWER_TOTAL]; -static DEFINE_PER_CPU(ulong[POWER_TOTAL], power); -static DEFINE_PER_CPU(ulong[POWER_TOTAL], prev); -static DEFINE_PER_CPU(int *, powerGet); - -GATOR_DEFINE_PROBE(cpu_frequency, TP_PROTO(unsigned int frequency, unsigned int cpu)) -{ - per_cpu(power, cpu)[POWER_CPU_FREQ] = frequency * 1000; -} - -GATOR_DEFINE_PROBE(cpu_idle, TP_PROTO(unsigned int state, unsigned int cpu)) -{ - per_cpu(power, cpu)[POWER_CPU_IDLE] = state; -} - -static int gator_events_power_create_files(struct super_block *sb, struct dentry *root) -{ - struct dentry *dir; - - // cpu_frequency - dir = gatorfs_mkdir(sb, root, "Linux_power_cpu_freq"); - if (!dir) { - return -1; - } - gatorfs_create_ulong(sb, dir, "enabled", &power_cpu_enabled[POWER_CPU_FREQ]); - gatorfs_create_ro_ulong(sb, dir, "key", &power_cpu_key[POWER_CPU_FREQ]); - - // cpu_idle - dir = gatorfs_mkdir(sb, root, "Linux_power_cpu_idle"); - if (!dir) { - return -1; - } - gatorfs_create_ulong(sb, dir, "enabled", &power_cpu_enabled[POWER_CPU_IDLE]); - gatorfs_create_ro_ulong(sb, dir, "key", &power_cpu_key[POWER_CPU_IDLE]); - - return 0; -} - -static int gator_events_power_populate(int cpu, int** buffer) -{ - int i, len = 0; - - for (i = 0; i < POWER_TOTAL; i++) { - if (power_cpu_enabled[i]) { - if (per_cpu(power, cpu)[i] != per_cpu(prev, cpu)[i]) { - per_cpu(prev, cpu)[i] = per_cpu(power, cpu)[i]; - per_cpu(powerGet, cpu)[len++] = power_cpu_key[i]; - per_cpu(powerGet, cpu)[len++] = per_cpu(power, cpu)[i]; - } - } - } - - if (buffer) - *buffer = per_cpu(powerGet, cpu); - - return len; -} - -static int gator_events_power_online(int** buffer) -{ - int i, cpu = smp_processor_id(); - for (i = 0; i < POWER_TOTAL; i++) - per_cpu(prev, cpu)[i] = -1; - per_cpu(power, cpu)[POWER_CPU_FREQ] = cpufreq_quick_get(cpu) * 1000; - return gator_events_power_populate(cpu, buffer); -} - -static int gator_events_power_offline(int** buffer) -{ - int cpu = smp_processor_id(); - // Set frequency to zero on an offline - per_cpu(power, cpu)[POWER_CPU_FREQ] = 0; - return gator_events_power_populate(cpu, buffer); -} - -static int gator_events_power_start(void) -{ - int cpu; - - for_each_present_cpu(cpu) { - per_cpu(powerGet, cpu) = kmalloc(POWER_TOTAL * 2, GFP_KERNEL); - if (!per_cpu(powerGet, cpu)) - return -1; - } - - // register tracepoints - if (power_cpu_enabled[POWER_CPU_FREQ]) - if (GATOR_REGISTER_TRACE(cpu_frequency)) - goto fail_cpu_frequency_exit; - if (power_cpu_enabled[POWER_CPU_IDLE]) - if (GATOR_REGISTER_TRACE(cpu_idle)) - goto fail_cpu_idle_exit; - pr_debug("gator: registered power event tracepoints\n"); - - return 0; - - // unregister tracepoints on error -fail_cpu_idle_exit: - GATOR_UNREGISTER_TRACE(cpu_frequency); -fail_cpu_frequency_exit: - pr_err("gator: power event tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n"); - - return -1; -} - -static void gator_events_power_stop(void) -{ - int i, cpu; - if (power_cpu_enabled[POWER_CPU_FREQ]) - GATOR_UNREGISTER_TRACE(cpu_frequency); - if (power_cpu_enabled[POWER_CPU_IDLE]) - GATOR_UNREGISTER_TRACE(cpu_idle); - pr_debug("gator: unregistered power event tracepoints\n"); - - for (i = 0; i < POWER_TOTAL; i++) { - power_cpu_enabled[i] = 0; - } - - for_each_present_cpu(cpu) { - kfree(per_cpu(powerGet, cpu)); - } -} - -static int gator_events_power_read(int **buffer) -{ - return gator_events_power_populate(smp_processor_id(), buffer); -} - -static struct gator_interface gator_events_power_interface = { - .create_files = gator_events_power_create_files, - .online = gator_events_power_online, - .offline = gator_events_power_offline, - .start = gator_events_power_start, - .stop = gator_events_power_stop, - .read = gator_events_power_read, -}; -#endif - -int gator_events_power_init(void) -{ -#if (GATOR_CPU_FREQ_SUPPORT) - int i; - for (i = 0; i < POWER_TOTAL; i++) { - power_cpu_enabled[i] = 0; - power_cpu_key[i] = gator_events_get_key(); - } - - return gator_events_install(&gator_events_power_interface); -#else - return -1; -#endif -} -gator_events_init(gator_events_power_init); diff --git a/driver/gator_events_scorpion.c b/driver/gator_events_scorpion.c index 477e7c9..ed0d8de 100644 --- a/driver/gator_events_scorpion.c +++ b/driver/gator_events_scorpion.c @@ -34,7 +34,6 @@ static unsigned long pmnc_enabled[CNTMAX]; static unsigned long pmnc_event[CNTMAX]; static unsigned long pmnc_key[CNTMAX]; -static DEFINE_PER_CPU(int[CNTMAX], perfPrev); static DEFINE_PER_CPU(int[CNTMAX * 2], perfCnt); enum scorpion_perf_types { @@ -538,8 +537,6 @@ static int gator_events_scorpion_online(int** buffer) for (cnt = CCNT; cnt < CNTMAX; cnt++) { unsigned long event; - per_cpu(perfPrev, smp_processor_id())[cnt] = 0; - if (!pmnc_enabled[cnt]) continue; @@ -574,7 +571,7 @@ static int gator_events_scorpion_online(int** buffer) value = 0; } scorpion_pmnc_reset_counter(cnt); - per_cpu(perfPrev, cpu)[cnt] = 0; + per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; per_cpu(perfCnt, cpu)[len++] = 0; } @@ -607,6 +604,11 @@ static int gator_events_scorpion_read(int **buffer) int cnt, len = 0; int cpu = smp_processor_id(); + // a context switch may occur before the online hotplug event, thus need to check that the pmu is enabled + if (!(scorpion_pmnc_read() & PMNC_E)) { + return 0; + } + for (cnt = 0; cnt < pmnc_counters; cnt++) { if (pmnc_enabled[cnt]) { int value; @@ -618,11 +620,9 @@ static int gator_events_scorpion_read(int **buffer) value = 0; } scorpion_pmnc_reset_counter(cnt); - if (value != per_cpu(perfPrev, cpu)[cnt]) { - per_cpu(perfPrev, cpu)[cnt] = value; - per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; - per_cpu(perfCnt, cpu)[len++] = value; - } + + per_cpu(perfCnt, cpu)[len++] = pmnc_key[cnt]; + per_cpu(perfCnt, cpu)[len++] = value; } } diff --git a/driver/gator_fs.c b/driver/gator_fs.c index 8277c3a..39adfbe 100644 --- a/driver/gator_fs.c +++ b/driver/gator_fs.c @@ -233,9 +233,17 @@ static int gatorfs_fill_super(struct super_block *sb, void *data, int silent) return -ENOMEM; root_inode->i_op = &simple_dir_inode_operations; root_inode->i_fop = &simple_dir_operations; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) root_dentry = d_alloc_root(root_inode); +#else + root_dentry = d_make_root(root_inode); +#endif + if (!root_dentry) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) iput(root_inode); +#endif return -ENOMEM; } diff --git a/driver/gator_hrtimer_gator.c b/driver/gator_hrtimer_gator.c old mode 100644 new mode 100755 index 5896b3c..846fba4 --- a/driver/gator_hrtimer_gator.c +++ b/driver/gator_hrtimer_gator.c @@ -13,6 +13,7 @@ void (*callback)(void); DEFINE_PER_CPU(struct hrtimer, percpu_hrtimer); +DEFINE_PER_CPU(int, hrtimer_is_active); static ktime_t profiling_interval; static void gator_hrtimer_online(int cpu); static void gator_hrtimer_offline(int cpu); @@ -32,11 +33,16 @@ static void gator_hrtimer_switch_cpus_online(void *unused) static void gator_hrtimer_online(int cpu) { struct hrtimer *hrtimer = &per_cpu(percpu_hrtimer, cpu); + if (cpu != smp_processor_id()) { smp_call_function_single(cpu, gator_hrtimer_switch_cpus_online, NULL, 1); return; } + if (per_cpu(hrtimer_is_active, cpu) || profiling_interval.tv64 == 0) + return; + + per_cpu(hrtimer_is_active, cpu) = 1; hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); hrtimer->function = gator_hrtimer_notify; hrtimer_start(hrtimer, profiling_interval, HRTIMER_MODE_REL_PINNED); @@ -50,20 +56,35 @@ static void gator_hrtimer_switch_cpus_offline(void *unused) static void gator_hrtimer_offline(int cpu) { struct hrtimer *hrtimer = &per_cpu(percpu_hrtimer, cpu); + if (cpu != smp_processor_id()) { smp_call_function_single(cpu, gator_hrtimer_switch_cpus_offline, NULL, 1); return; } + if (!per_cpu(hrtimer_is_active, cpu)) + return; + + per_cpu(hrtimer_is_active, cpu) = 0; hrtimer_cancel(hrtimer); } static int gator_hrtimer_init(int interval, void (*func)(void)) { + int cpu; + (callback) = (func); + for_each_present_cpu(cpu) { + per_cpu(hrtimer_is_active, cpu) = 0; + } + // calculate profiling interval - profiling_interval = ns_to_ktime(1000000000UL / interval); + if (interval > 0) { + profiling_interval = ns_to_ktime(1000000000UL / interval); + } else { + profiling_interval.tv64 = 0; + } return 0; } diff --git a/driver/gator_main.c b/driver/gator_main.c index fff2d19..7d48812 100644 --- a/driver/gator_main.c +++ b/driver/gator_main.c @@ -7,7 +7,7 @@ * */ -static unsigned long gator_protocol_version = 8; +static unsigned long gator_protocol_version = 9; #include #include @@ -39,7 +39,7 @@ static unsigned long gator_protocol_version = 8; #endif #ifndef CONFIG_HIGH_RES_TIMERS -#error gator requires the kernel to have CONFIG_HIGH_RES_TIMERS defined +#error gator requires the kernel to have CONFIG_HIGH_RES_TIMERS defined to support PC sampling #endif #if defined(__arm__) && defined(CONFIG_SMP) && !defined(CONFIG_LOCAL_TIMERS) @@ -48,37 +48,39 @@ static unsigned long gator_protocol_version = 8; #if (GATOR_PERF_SUPPORT) && (!(GATOR_PERF_PMU_SUPPORT)) #ifndef CONFIG_PERF_EVENTS -#warning gator requires the kernel to have CONFIG_PERF_EVENTS defined to support pmu hardware counters +#error gator requires the kernel to have CONFIG_PERF_EVENTS defined to support pmu hardware counters #elif !defined CONFIG_HW_PERF_EVENTS -#warning gator requires the kernel to have CONFIG_HW_PERF_EVENTS defined to support pmu hardware counters +#error gator requires the kernel to have CONFIG_HW_PERF_EVENTS defined to support pmu hardware counters #endif #endif -#if (!(GATOR_CPU_FREQ_SUPPORT)) -#warning gator requires kernel version 2.6.38 or greater and CONFIG_CPU_FREQ defined in order to enable the CPU Freq timeline chart -#endif - /****************************************************************************** * DEFINES ******************************************************************************/ -#define TIMER_BUFFER_SIZE_DEFAULT (512*1024) -#define EVENT_BUFFER_SIZE_DEFAULT (128*1024) +#define BACKTRACE_BUFFER_SIZE (128*1024) +#define COUNTER_BUFFER_SIZE (128*1024) +#define ANNOTATE_BUFFER_SIZE (64*1024) // annotate counters have the core as part of the data and the core value in the frame header may be discarded +#define SCHED_TRACE_BUFFER_SIZE (128*1024) +#define GPU_TRACE_BUFFER_SIZE (64*1024) +#define COUNTER2_BUFFER_SIZE (64*1024) // counters2 counters have the core as part of the data and the core value in the frame header may be discarded +#define WFI_BUFFER_SIZE (32*1024) // wfi counters have the core as part of the data and the core value in the frame header may be discarded #define NO_COOKIE 0UL #define INVALID_COOKIE ~0UL -#define FRAME_HRTIMER 1 -#define FRAME_EVENT 2 -#define FRAME_ANNOTATE 3 +#define FRAME_BACKTRACE 1 +#define FRAME_COUNTER 2 +#define FRAME_ANNOTATE 3 +#define FRAME_SCHED_TRACE 4 +#define FRAME_GPU_TRACE 5 +#define FRAME_COUNTER2 6 +#define FRAME_WFI 7 -#define MESSAGE_COOKIE 1 -#define MESSAGE_COUNTERS 3 -#define MESSAGE_START_BACKTRACE 5 -#define MESSAGE_END_BACKTRACE 7 -#define MESSAGE_SCHEDULER_TRACE 9 -#define MESSAGE_PID_NAME 11 -#define MESSAGE_GPU_TRACE 13 -#define MESSAGE_OVERFLOW 127 +#define MESSAGE_COOKIE 1 +#define MESSAGE_START_BACKTRACE 5 +#define MESSAGE_END_BACKTRACE 7 +#define MESSAGE_SUMMARY 9 +#define MESSAGE_PID_NAME 11 #define MAXSIZE_PACK32 5 #define MAXSIZE_PACK64 9 @@ -89,7 +91,7 @@ static unsigned long gator_protocol_version = 8; #define PC_REG regs->ip #endif -enum {TIMER_BUF, EVENT_BUF, NUM_GATOR_BUFS}; +enum {BACKTRACE_BUF, COUNTER_BUF, SCHED_TRACE_BUF, GPU_TRACE_BUF, ANNOTATE_BUF, COUNTER2_BUF, WFI_BUF, NUM_GATOR_BUFS}; /****************************************************************************** * Globals @@ -101,25 +103,26 @@ static unsigned long gator_backtrace_depth; static unsigned long gator_started; static unsigned long gator_buffer_opened; static unsigned long gator_timer_count; -static unsigned long gator_streaming; +static unsigned long gator_response_type; static DEFINE_MUTEX(start_mutex); static DEFINE_MUTEX(gator_buffer_mutex); bool event_based_sampling; static DECLARE_WAIT_QUEUE_HEAD(gator_buffer_wait); - -static void buffer_check(int cpu, int buftype); +static LIST_HEAD(gator_events); /****************************************************************************** * Prototypes ******************************************************************************/ +static void buffer_check(int cpu, int buftype); +static int buffer_bytes_available(int cpu, int buftype); static bool buffer_check_space(int cpu, int buftype, int bytes); +static int contiguous_space_available(int cpu, int bufytpe); static void gator_buffer_write_packed_int(int cpu, int buftype, unsigned int x); static void gator_buffer_write_packed_int64(int cpu, int buftype, unsigned long long x); +static void gator_buffer_write_bytes(int cpu, int buftype, char *x, int len); static void gator_buffer_write_string(int cpu, int buftype, char *x); -static int gator_write_packed_int(char *buffer, unsigned int x); -static int gator_write_packed_int64(char *buffer, unsigned long long x); static void gator_add_trace(int cpu, int buftype, unsigned int address); static void gator_add_sample(int cpu, int buftype, struct pt_regs * const regs); static uint64_t gator_get_time(void); @@ -131,15 +134,16 @@ static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], gator_buffer_write); static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], gator_buffer_commit); static DEFINE_PER_CPU(int[NUM_GATOR_BUFS], buffer_space_available); static DEFINE_PER_CPU(char *[NUM_GATOR_BUFS], gator_buffer); -static DEFINE_PER_CPU(uint64_t, emit_overflow); /****************************************************************************** * Application Includes ******************************************************************************/ +#include "gator_marshaling.c" #include "gator_hrtimer_perf.c" #include "gator_hrtimer_gator.c" #include "gator_cookies.c" #include "gator_trace_sched.c" +#include "gator_trace_power.c" #include "gator_trace_gpu.c" #include "gator_backtrace.c" #include "gator_annotate.c" @@ -179,10 +183,10 @@ static bool buffer_commit_ready(int* cpu, int* buftype) /****************************************************************************** * Buffer management ******************************************************************************/ -static bool buffer_check_space(int cpu, int buftype, int bytes) +static int buffer_bytes_available(int cpu, int buftype) { int remaining, filled; - + filled = per_cpu(gator_buffer_write, cpu)[buftype] - per_cpu(gator_buffer_read, cpu)[buftype]; if (filled < 0) { filled += gator_buffer_size[buftype]; @@ -198,12 +202,24 @@ static bool buffer_check_space(int cpu, int buftype, int bytes) remaining -= 2000; } + return remaining; +} + +static int contiguous_space_available(int cpu, int buftype) +{ + int remaining = buffer_bytes_available(cpu, buftype); + int contiguous = gator_buffer_size[buftype] - per_cpu(gator_buffer_write, cpu)[buftype]; + if (remaining < contiguous) + return remaining; + else + return contiguous; +} + +static bool buffer_check_space(int cpu, int buftype, int bytes) +{ + int remaining = buffer_bytes_available(cpu, buftype); + if (remaining < bytes) { - if (per_cpu(buffer_space_available, cpu)[buftype] == true) { - // overflow packet to be emitted at a later time, as we may be in the middle of writing a message, e.g. counters - per_cpu(emit_overflow, cpu) = gator_get_time(); - pr_err("overflow: remaining = %d\n", gator_buffer_size[buftype] - filled); - } per_cpu(buffer_space_available, cpu)[buftype] = false; } else { per_cpu(buffer_space_available, cpu)[buftype] = true; @@ -238,19 +254,33 @@ static void gator_buffer_header(int cpu, int buftype) { int frame; - if (buftype == TIMER_BUF) - frame = FRAME_HRTIMER; - else if (buftype == EVENT_BUF) - frame = FRAME_EVENT; + if (buftype == BACKTRACE_BUF) + frame = FRAME_BACKTRACE; + else if (buftype == COUNTER_BUF) + frame = FRAME_COUNTER; + else if (buftype == ANNOTATE_BUF) + frame = FRAME_ANNOTATE; + else if (buftype == SCHED_TRACE_BUF) + frame = FRAME_SCHED_TRACE; + else if (buftype == GPU_TRACE_BUF) + frame = FRAME_GPU_TRACE; + else if (buftype == COUNTER2_BUF) + frame = FRAME_COUNTER2; + else if (buftype == WFI_BUF) + frame = FRAME_WFI; else frame = -1; - gator_buffer_write_packed_int(cpu, buftype, frame); - gator_buffer_write_packed_int(cpu, buftype, cpu); + if (per_cpu(gator_buffer, cpu)[buftype]) { + marshal_frame(cpu, buftype, frame); + } } static void gator_commit_buffer(int cpu, int buftype) { + if (!per_cpu(gator_buffer, cpu)[buftype]) + return; + per_cpu(gator_buffer_commit, cpu)[buftype] = per_cpu(gator_buffer_write, cpu)[buftype]; gator_buffer_header(cpu, buftype); wake_up(&gator_buffer_wait); @@ -276,8 +306,7 @@ static void gator_add_trace(int cpu, int buftype, unsigned int address) offset = address; } - gator_buffer_write_packed_int(cpu, buftype, offset & ~1); - gator_buffer_write_packed_int(cpu, buftype, cookie); + marshal_backtrace(offset & ~1, cookie); } static void gator_add_sample(int cpu, int buftype, struct pt_regs * const regs) @@ -288,12 +317,8 @@ static void gator_add_sample(int cpu, int buftype, struct pt_regs * const regs) if (!regs) return; - gator_buffer_write_packed_int(cpu, buftype, MESSAGE_START_BACKTRACE); - gator_buffer_write_packed_int64(cpu, buftype, gator_get_time()); - gator_buffer_write_packed_int(cpu, buftype, exec_cookie); - gator_buffer_write_packed_int(cpu, buftype, (unsigned int)current->tgid); - gator_buffer_write_packed_int(cpu, buftype, (unsigned int)current->pid); - gator_buffer_write_packed_int(cpu, buftype, inKernel); + if (!marshal_backtrace_header(exec_cookie, current->tgid, current->pid, inKernel)) + return; if (inKernel) { kernel_backtrace(cpu, buftype, regs); @@ -306,142 +331,54 @@ static void gator_add_sample(int cpu, int buftype, struct pt_regs * const regs) arm_backtrace_eabi(cpu, buftype, regs, gator_backtrace_depth); } - gator_buffer_write_packed_int(cpu, buftype, MESSAGE_END_BACKTRACE); + marshal_backtrace_footer(); } /****************************************************************************** * hrtimer interrupt processing ******************************************************************************/ -static LIST_HEAD(gator_events); - static void gator_timer_interrupt(void) { struct pt_regs * const regs = get_irq_regs(); int cpu = smp_processor_id(); - int *buffer, len, i, buftype = TIMER_BUF; - long long *buffer64; - struct gator_interface *gi; - - // Output scheduler trace - len = gator_trace_sched_read(&buffer64); - if (len > 0 && buffer_check_space(cpu, buftype, len * MAXSIZE_PACK64 + 2 * MAXSIZE_PACK32)) { - gator_buffer_write_packed_int(cpu, buftype, MESSAGE_SCHEDULER_TRACE); - gator_buffer_write_packed_int(cpu, buftype, len); - for (i = 0; i < len; i++) { - gator_buffer_write_packed_int64(cpu, buftype, buffer64[i]); - } - } - - // Output GPU trace - len = gator_trace_gpu_read(&buffer64); - if (len > 0 && buffer_check_space(cpu, buftype, len * MAXSIZE_PACK64 + 2 * MAXSIZE_PACK32)) { - gator_buffer_write_packed_int(cpu, buftype, MESSAGE_GPU_TRACE); - gator_buffer_write_packed_int(cpu, buftype, len); - for (i = 0; i < len; i++) { - gator_buffer_write_packed_int64(cpu, buftype, buffer64[i]); - } - } - - // Output counters - if (buffer_check_space(cpu, buftype, MAXSIZE_PACK32 * 2 + MAXSIZE_PACK64)) { - gator_buffer_write_packed_int(cpu, buftype, MESSAGE_COUNTERS); - gator_buffer_write_packed_int64(cpu, buftype, gator_get_time()); - list_for_each_entry(gi, &gator_events, list) { - if (gi->read) { - len = gi->read(&buffer); - if (len > 0 && buffer_check_space(cpu, buftype, len * MAXSIZE_PACK32 + MAXSIZE_PACK32)) { - gator_buffer_write_packed_int(cpu, buftype, len); - for (i = 0; i < len; i++) { - gator_buffer_write_packed_int(cpu, buftype, buffer[i]); - } - } - } else if (gi->read64) { - len = gi->read64(&buffer64); - if (len > 0 && buffer_check_space(cpu, buftype, len * MAXSIZE_PACK64 + MAXSIZE_PACK32)) { - gator_buffer_write_packed_int(cpu, buftype, len); - for (i = 0; i < len; i++) { - gator_buffer_write_packed_int64(cpu, buftype, buffer64[i]); - } - } - } - } - gator_buffer_write_packed_int(cpu, buftype, 0); - } // Output backtrace - if (!event_based_sampling && buffer_check_space(cpu, buftype, gator_backtrace_depth * 2 * MAXSIZE_PACK32)) - gator_add_sample(cpu, buftype, regs); - - // Overflow message - if (per_cpu(emit_overflow, cpu)) { - gator_buffer_write_packed_int(cpu, buftype, MESSAGE_OVERFLOW); - gator_buffer_write_packed_int64(cpu, buftype, per_cpu(emit_overflow, cpu)); - per_cpu(emit_overflow, cpu) = 0; - } + gator_add_sample(cpu, BACKTRACE_BUF, regs); - // Check and commit; generally, commit is set to occur once per second - buffer_check(cpu, buftype); + // Collect counters + collect_counters(); } -DEFINE_PER_CPU(int, hrtimer_is_active); -static int hrtimer_running; +static int gator_running; // This function runs in interrupt context and on the appropriate core static void gator_timer_offline(void* unused) { + struct gator_interface *gi; int i, len, cpu = smp_processor_id(); int* buffer; - long long* buffer64; - - if (per_cpu(hrtimer_is_active, cpu)) { - struct gator_interface *gi; - gator_hrtimer_offline(cpu); - per_cpu(hrtimer_is_active, cpu) = 0; - - // Output scheduler trace - len = gator_trace_sched_offline(&buffer64); - if (len > 0 && buffer_check_space(cpu, TIMER_BUF, len * MAXSIZE_PACK64 + 2 * MAXSIZE_PACK32)) { - gator_buffer_write_packed_int(cpu, TIMER_BUF, MESSAGE_SCHEDULER_TRACE); - gator_buffer_write_packed_int(cpu, TIMER_BUF, len); - for (i = 0; i < len; i++) { - gator_buffer_write_packed_int64(cpu, TIMER_BUF, buffer64[i]); - } - } - // Output GPU trace - len = gator_trace_gpu_offline(&buffer64); - if (len > 0 && buffer_check_space(cpu, TIMER_BUF, len * MAXSIZE_PACK64 + 2 * MAXSIZE_PACK32)) { - gator_buffer_write_packed_int(cpu, TIMER_BUF, MESSAGE_GPU_TRACE); - gator_buffer_write_packed_int(cpu, TIMER_BUF, len); - for (i = 0; i < len; i++) { - gator_buffer_write_packed_int64(cpu, TIMER_BUF, buffer64[i]); - } - } + gator_trace_sched_offline(); + gator_trace_power_offline(); - // offline any events and output counters - gator_buffer_write_packed_int(cpu, TIMER_BUF, MESSAGE_COUNTERS); - gator_buffer_write_packed_int64(cpu, TIMER_BUF, gator_get_time()); + gator_hrtimer_offline(cpu); + + // Offline any events and output counters + if (marshal_event_header()) { list_for_each_entry(gi, &gator_events, list) { if (gi->offline) { len = gi->offline(&buffer); - if (len > 0 && buffer_check_space(cpu, TIMER_BUF, len * MAXSIZE_PACK32 + MAXSIZE_PACK32)) { - gator_buffer_write_packed_int(cpu, TIMER_BUF, len); - for (i = 0; i < len; i++) - gator_buffer_write_packed_int(cpu, TIMER_BUF, buffer[i]); - } + marshal_event(len, buffer); } } - gator_buffer_write_packed_int(cpu, TIMER_BUF, 0); - - gator_commit_buffer(cpu, TIMER_BUF); } - if (event_based_sampling) { - gator_commit_buffer(cpu, EVENT_BUF); - } + // Flush all buffers on this core + for (i = 0; i < NUM_GATOR_BUFS; i++) + gator_commit_buffer(cpu, i); } -// This function runs in interrupt context and may be running on a core other than core 'cpu' +// This function runs in process context and may be running on a core other than core 'cpu' static void gator_timer_offline_dispatch(int cpu) { struct gator_interface *gi; @@ -457,13 +394,13 @@ static void gator_timer_stop(void) { int cpu; - if (hrtimer_running) { + if (gator_running) { on_each_cpu(gator_timer_offline, NULL, 1); for_each_online_cpu(cpu) { gator_timer_offline_dispatch(cpu); } - hrtimer_running = 0; + gator_running = 0; gator_hrtimer_shutdown(); } } @@ -471,32 +408,23 @@ static void gator_timer_stop(void) // This function runs in interrupt context and on the appropriate core static void gator_timer_online(void* unused) { - int i, len, cpu = smp_processor_id(); + struct gator_interface *gi; + int len, cpu = smp_processor_id(); int* buffer; - if (!per_cpu(hrtimer_is_active, cpu)) { - struct gator_interface *gi; + gator_trace_power_online(); - // online any events and output counters - gator_buffer_write_packed_int(cpu, TIMER_BUF, MESSAGE_COUNTERS); - gator_buffer_write_packed_int64(cpu, TIMER_BUF, gator_get_time()); + // online any events and output counters + if (marshal_event_header()) { list_for_each_entry(gi, &gator_events, list) { if (gi->online) { len = gi->online(&buffer); - if (len > 0 && buffer_check_space(cpu, TIMER_BUF, len * MAXSIZE_PACK32 + MAXSIZE_PACK32)) { - gator_buffer_write_packed_int(cpu, TIMER_BUF, len); - for (i = 0; i < len; i++) - gator_buffer_write_packed_int(cpu, TIMER_BUF, buffer[i]); - } + marshal_event(len, buffer); } } - gator_buffer_write_packed_int(cpu, TIMER_BUF, 0); - - gator_event_sampling_online(); - - gator_hrtimer_online(cpu); - per_cpu(hrtimer_is_active, cpu) = 1; } + + gator_hrtimer_online(cpu); } // This function runs in interrupt context and may be running on a core other than core 'cpu' @@ -511,21 +439,22 @@ static void gator_timer_online_dispatch(int cpu) gator_event_sampling_online_dispatch(cpu); } -int gator_timer_start(unsigned long setup) +int gator_timer_start(unsigned long sample_rate) { int cpu; - if (!setup) { - pr_err("gator: cannot start due to a system tick value of zero\n"); - return -1; - } else if (hrtimer_running) { - pr_notice("gator: high res timer already running\n"); + if (gator_running) { + pr_notice("gator: already running\n"); return 0; } - hrtimer_running = 1; + gator_running = 1; - if (gator_hrtimer_init(setup, gator_timer_interrupt) == -1) + // event based sampling trumps hr timer based sampling + if (event_based_sampling) + sample_rate = 0; + + if (gator_hrtimer_init(sample_rate, gator_timer_interrupt) == -1) return -1; for_each_online_cpu(cpu) { @@ -550,7 +479,7 @@ static uint64_t gator_get_time(void) /****************************************************************************** * cpu hotplug and pm notifiers ******************************************************************************/ -static int __cpuinit gator_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) +static int __cpuinit gator_hotcpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) { long cpu = (long)hcpu; @@ -570,8 +499,8 @@ static int __cpuinit gator_cpu_notify(struct notifier_block *self, unsigned long return NOTIFY_OK; } -static struct notifier_block __refdata gator_cpu_notifier = { - .notifier_call = gator_cpu_notify, +static struct notifier_block __refdata gator_hotcpu_notifier = { + .notifier_call = gator_hotcpu_notify, }; // n.b. calling "on_each_cpu" only runs on those that are online @@ -583,7 +512,7 @@ static int gator_pm_notify(struct notifier_block *nb, unsigned long event, void switch (event) { case PM_HIBERNATION_PREPARE: case PM_SUSPEND_PREPARE: - unregister_hotcpu_notifier(&gator_cpu_notifier); + unregister_hotcpu_notifier(&gator_hotcpu_notifier); unregister_scheduler_tracepoints(); on_each_cpu(gator_timer_offline, NULL, 1); for_each_online_cpu(cpu) { @@ -597,7 +526,7 @@ static int gator_pm_notify(struct notifier_block *nb, unsigned long event, void } on_each_cpu(gator_timer_online, NULL, 1); register_scheduler_tracepoints(); - register_hotcpu_notifier(&gator_cpu_notifier); + register_hotcpu_notifier(&gator_hotcpu_notifier); break; } @@ -611,7 +540,7 @@ static struct notifier_block gator_pm_notifier = { static int gator_notifier_start(void) { int retval; - retval = register_hotcpu_notifier(&gator_cpu_notifier); + retval = register_hotcpu_notifier(&gator_hotcpu_notifier); if (retval == 0) retval = register_pm_notifier(&gator_pm_notifier); return retval; @@ -620,12 +549,30 @@ static int gator_notifier_start(void) static void gator_notifier_stop(void) { unregister_pm_notifier(&gator_pm_notifier); - unregister_hotcpu_notifier(&gator_cpu_notifier); + unregister_hotcpu_notifier(&gator_hotcpu_notifier); } /****************************************************************************** * Main ******************************************************************************/ +static void gator_summary(void) +{ + uint64_t timestamp, uptime = 0; + struct timespec uptime_ts; + void (*m2b)(struct timespec *ts); + + timestamp = gator_get_time(); + + do_posix_clock_monotonic_gettime(&uptime_ts); + m2b = symbol_get(monotonic_to_bootbased); + if (m2b) { + m2b(&uptime_ts); + uptime = (long long)uptime_ts.tv_sec * 1000000000 + uptime_ts.tv_nsec; + } + + marshal_summary(timestamp, uptime); +} + int gator_events_install(struct gator_interface *interface) { list_add_tail(&interface->list, &gator_events); @@ -635,7 +582,8 @@ int gator_events_install(struct gator_interface *interface) int gator_events_get_key(void) { - static int key; + // key of zero is reserved as a timestamp + static int key = 1; return key++; } @@ -644,21 +592,31 @@ static int gator_init(void) { int i; - if (gator_annotate_init()) - return -1; - // events sources (gator_events.h, generated by gator_events.sh) for (i = 0; i < ARRAY_SIZE(gator_events_list); i++) if (gator_events_list[i]) gator_events_list[i](); + gator_trace_power_init(); + return 0; } static int gator_start(void) { + unsigned long cpu, i; struct gator_interface *gi; + // Initialize the buffer with the frame type and core + for_each_present_cpu(cpu) { + for (i = 0; i < NUM_GATOR_BUFS; i++) { + gator_buffer_header(cpu, i); + } + } + + // Capture the start time + gator_summary(); + // start all events list_for_each_entry(gi, &gator_events, list) { if (gi->start && gi->start() != 0) { @@ -683,6 +641,8 @@ static int gator_start(void) goto annotate_failure; if (gator_trace_sched_start()) goto sched_failure; + if (gator_trace_power_start()) + goto power_failure; if (gator_trace_gpu_start()) goto gpu_failure; if (gator_event_sampling_start()) @@ -701,6 +661,8 @@ timer_failure: event_sampling_failure: gator_trace_gpu_stop(); gpu_failure: + gator_trace_power_stop(); +power_failure: gator_trace_sched_stop(); sched_failure: gator_annotate_stop(); @@ -727,6 +689,7 @@ static void gator_stop(void) gator_annotate_stop(); gator_trace_sched_stop(); + gator_trace_power_stop(); gator_trace_gpu_stop(); gator_event_sampling_stop(); @@ -735,11 +698,6 @@ static void gator_stop(void) gator_timer_stop(); } -static void gator_exit(void) -{ - gator_annotate_exit(); -} - /****************************************************************************** * Filesystem ******************************************************************************/ @@ -751,33 +709,52 @@ static int gator_op_setup(void) mutex_lock(&start_mutex); - gator_buffer_size[TIMER_BUF] = userspace_buffer_size; - gator_buffer_mask[TIMER_BUF] = userspace_buffer_size - 1; + gator_buffer_size[BACKTRACE_BUF] = BACKTRACE_BUFFER_SIZE; + gator_buffer_mask[BACKTRACE_BUF] = BACKTRACE_BUFFER_SIZE - 1; - // must be a power of 2 - if (gator_buffer_size[TIMER_BUF] & (gator_buffer_size[TIMER_BUF] - 1)) { - err = -ENOEXEC; - goto setup_error; - } + gator_buffer_size[COUNTER_BUF] = COUNTER_BUFFER_SIZE; + gator_buffer_mask[COUNTER_BUF] = COUNTER_BUFFER_SIZE - 1; + + gator_buffer_size[SCHED_TRACE_BUF] = SCHED_TRACE_BUFFER_SIZE; + gator_buffer_mask[SCHED_TRACE_BUF] = SCHED_TRACE_BUFFER_SIZE - 1; + + gator_buffer_size[GPU_TRACE_BUF] = GPU_TRACE_BUFFER_SIZE; + gator_buffer_mask[GPU_TRACE_BUF] = GPU_TRACE_BUFFER_SIZE - 1; - gator_buffer_size[EVENT_BUF] = EVENT_BUFFER_SIZE_DEFAULT; - gator_buffer_mask[EVENT_BUF] = gator_buffer_size[EVENT_BUF] - 1; + gator_buffer_size[ANNOTATE_BUF] = ANNOTATE_BUFFER_SIZE; + gator_buffer_mask[ANNOTATE_BUF] = ANNOTATE_BUFFER_SIZE - 1; + + gator_buffer_size[COUNTER2_BUF] = COUNTER2_BUFFER_SIZE; + gator_buffer_mask[COUNTER2_BUF] = COUNTER2_BUFFER_SIZE - 1; + + gator_buffer_size[WFI_BUF] = WFI_BUFFER_SIZE; + gator_buffer_mask[WFI_BUF] = WFI_BUFFER_SIZE - 1; // Initialize percpu per buffer variables for (i = 0; i < NUM_GATOR_BUFS; i++) { + // Verify buffers are a power of 2 + if (gator_buffer_size[i] & (gator_buffer_size[i] - 1)) { + err = -ENOEXEC; + goto setup_error; + } + for_each_present_cpu(cpu) { + per_cpu(gator_buffer_read, cpu)[i] = 0; + per_cpu(gator_buffer_write, cpu)[i] = 0; + per_cpu(gator_buffer_commit, cpu)[i] = 0; + per_cpu(buffer_space_available, cpu)[i] = true; + + // Annotation is a special case that only uses a single buffer + if (cpu > 0 && i == ANNOTATE_BUF) { + per_cpu(gator_buffer, cpu)[i] = NULL; + continue; + } + per_cpu(gator_buffer, cpu)[i] = vmalloc(gator_buffer_size[i]); if (!per_cpu(gator_buffer, cpu)[i]) { err = -ENOMEM; goto setup_error; } - - per_cpu(gator_buffer_read, cpu)[i] = 0; - per_cpu(gator_buffer_write, cpu)[i] = 0; - per_cpu(gator_buffer_commit, cpu)[i] = 0; - per_cpu(buffer_space_available, cpu)[i] = true; - per_cpu(emit_overflow, cpu) = 0; - gator_buffer_header(cpu, i); } } @@ -829,8 +806,6 @@ static void gator_shutdown(void) mutex_lock(&start_mutex); - gator_annotate_shutdown(); - for_each_present_cpu(cpu) { mutex_lock(&gator_buffer_mutex); for (i = 0; i < NUM_GATOR_BUFS; i++) { @@ -840,7 +815,6 @@ static void gator_shutdown(void) per_cpu(gator_buffer_write, cpu)[i] = 0; per_cpu(gator_buffer_commit, cpu)[i] = 0; per_cpu(buffer_space_available, cpu)[i] = true; - per_cpu(emit_overflow, cpu) = 0; } mutex_unlock(&gator_buffer_mutex); } @@ -932,7 +906,7 @@ static ssize_t userspace_buffer_read(struct file *file, char __user *buf, size_t count, loff_t *offset) { int retval = -EINVAL; - int commit = 0, length1, length2, read; + int commit = 0, length, length1, length2, read, byte, type_length; char *buffer1; char *buffer2 = NULL; int cpu, buftype; @@ -944,7 +918,7 @@ static ssize_t userspace_buffer_read(struct file *file, char __user *buf, // sleep until the condition is true or a signal is received // the condition is checked each time gator_buffer_wait is woken up buftype = cpu = -1; - wait_event_interruptible(gator_buffer_wait, buffer_commit_ready(&cpu, &buftype) || gator_annotate_ready() || !gator_started); + wait_event_interruptible(gator_buffer_wait, buffer_commit_ready(&cpu, &buftype) || !gator_started); if (signal_pending(current)) return -EINTR; @@ -954,33 +928,36 @@ static ssize_t userspace_buffer_read(struct file *file, char __user *buf, mutex_lock(&gator_buffer_mutex); - if (buftype != -1 && cpu != -1) { - read = per_cpu(gator_buffer_read, cpu)[buftype]; - commit = per_cpu(gator_buffer_commit, cpu)[buftype]; + if (buftype == -1 || cpu == -1) { + retval = 0; + goto out; + } - /* May happen if the buffer is freed during pending reads. */ - if (!per_cpu(gator_buffer, cpu)[buftype]) { - retval = -EFAULT; - goto out; - } + read = per_cpu(gator_buffer_read, cpu)[buftype]; + commit = per_cpu(gator_buffer_commit, cpu)[buftype]; - /* determine the size of two halves */ - length1 = commit - read; - buffer1 = &(per_cpu(gator_buffer, cpu)[buftype][read]); - buffer2 = &(per_cpu(gator_buffer, cpu)[buftype][0]); - if (length1 < 0) { - length1 = gator_buffer_size[buftype] - read; - length2 = commit; - } - } else if (gator_annotate_ready()) { - length1 = gator_annotate_read(&buffer1); - if (!length1) - goto out; - } else { - retval = 0; + /* May happen if the buffer is freed during pending reads. */ + if (!per_cpu(gator_buffer, cpu)[buftype]) { + retval = -EFAULT; goto out; } + /* determine the size of two halves */ + length1 = commit - read; + buffer1 = &(per_cpu(gator_buffer, cpu)[buftype][read]); + buffer2 = &(per_cpu(gator_buffer, cpu)[buftype][0]); + if (length1 < 0) { + length1 = gator_buffer_size[buftype] - read; + length2 = commit; + } + + // post-populate the length, which does not include the response type length nor the length itself, i.e. only the length of the payload + type_length = gator_response_type ? 1 : 0; + length = length1 + length2 - type_length - sizeof(int); + for (byte = 0; byte < sizeof(int); byte++) { + per_cpu(gator_buffer, cpu)[buftype][(read + type_length + byte) & gator_buffer_mask[buftype]] = (length >> byte * 8) & 0xFF; + } + /* start, middle or end */ if (length1 > 0) { if (copy_to_user(&buf[0], buffer1, length1)) { @@ -995,9 +972,7 @@ static ssize_t userspace_buffer_read(struct file *file, char __user *buf, } } - if (buftype != -1 && cpu != -1) - per_cpu(gator_buffer_read, cpu)[buftype] = commit; - + per_cpu(gator_buffer_read, cpu)[buftype] = commit; retval = length1 + length2; /* kick just in case we've lost an SMP event */ @@ -1055,8 +1030,8 @@ void gator_op_create_files(struct super_block *sb, struct dentry *root) for_each_present_cpu(cpu) { gator_cpu_cores++; } - userspace_buffer_size = TIMER_BUFFER_SIZE_DEFAULT; - gator_streaming = 1; + userspace_buffer_size = BACKTRACE_BUFFER_SIZE; + gator_response_type = 1; gatorfs_create_file(sb, root, "enable", &enable_fops); gatorfs_create_file(sb, root, "buffer", &gator_event_buffer_fops); @@ -1064,7 +1039,7 @@ void gator_op_create_files(struct super_block *sb, struct dentry *root) gatorfs_create_ulong(sb, root, "cpu_cores", &gator_cpu_cores); gatorfs_create_ulong(sb, root, "buffer_size", &userspace_buffer_size); gatorfs_create_ulong(sb, root, "tick", &gator_timer_count); - gatorfs_create_ulong(sb, root, "streaming", &gator_streaming); + gatorfs_create_ulong(sb, root, "response_type", &gator_response_type); gatorfs_create_ro_ulong(sb, root, "version", &gator_protocol_version); // Annotate interface @@ -1075,6 +1050,9 @@ void gator_op_create_files(struct super_block *sb, struct dentry *root) list_for_each_entry(gi, &gator_events, list) if (gi->create_files) gi->create_files(sb, dir); + + // Power interface + gator_trace_power_create_files(sb, dir); } /****************************************************************************** @@ -1098,7 +1076,6 @@ static void __exit gator_module_exit(void) { tracepoint_synchronize_unregister(); gatorfs_unregister(); - gator_exit(); } module_init(gator_module_init); diff --git a/driver/gator_marshaling.c b/driver/gator_marshaling.c new file mode 100755 index 0000000..630d142 --- /dev/null +++ b/driver/gator_marshaling.c @@ -0,0 +1,239 @@ +/** + * Copyright (C) ARM Limited 2012. 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. + * + */ + +static void marshal_summary(long long timestamp, long long uptime) { + int cpu = 0; + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, MESSAGE_SUMMARY); + gator_buffer_write_packed_int64(cpu, BACKTRACE_BUF, timestamp); + gator_buffer_write_packed_int64(cpu, BACKTRACE_BUF, uptime); +} + +static bool marshal_cookie_header(char* text) { + int cpu = smp_processor_id(); + return buffer_check_space(cpu, BACKTRACE_BUF, strlen(text) + 2 * MAXSIZE_PACK32); +} + +static void marshal_cookie(int cookie, char* text) { + int cpu = smp_processor_id(); + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, MESSAGE_COOKIE); + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, cookie); + gator_buffer_write_string(cpu, BACKTRACE_BUF, text); +} + +static void marshal_pid_name(int pid, char* name) { + unsigned long flags, cpu; + local_irq_save(flags); + cpu = smp_processor_id(); + if (buffer_check_space(cpu, BACKTRACE_BUF, TASK_COMM_LEN + 2 * MAXSIZE_PACK32 + MAXSIZE_PACK64)) { + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, MESSAGE_PID_NAME); + gator_buffer_write_packed_int64(cpu, BACKTRACE_BUF, gator_get_time()); + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, pid); + gator_buffer_write_string(cpu, BACKTRACE_BUF, name); + } + local_irq_restore(flags); +} + +static bool marshal_backtrace_header(int exec_cookie, int tgid, int pid, int inKernel) { + int cpu = smp_processor_id(); + if (buffer_check_space(cpu, BACKTRACE_BUF, gator_backtrace_depth * 2 * MAXSIZE_PACK32)) { + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, MESSAGE_START_BACKTRACE); + gator_buffer_write_packed_int64(cpu, BACKTRACE_BUF, gator_get_time()); + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, exec_cookie); + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, tgid); + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, pid); + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, inKernel); + return true; + } + + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, BACKTRACE_BUF); + + return false; +} + +static void marshal_backtrace(int address, int cookie) { + int cpu = smp_processor_id(); + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, address); + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, cookie); +} + +static void marshal_backtrace_footer(void) { + int cpu = smp_processor_id(); + gator_buffer_write_packed_int(cpu, BACKTRACE_BUF, MESSAGE_END_BACKTRACE); + + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, BACKTRACE_BUF); +} + +static bool marshal_event_header(void) { + unsigned long flags, cpu = smp_processor_id(); + bool retval = false; + + local_irq_save(flags); + if (buffer_check_space(cpu, COUNTER_BUF, MAXSIZE_PACK32 + MAXSIZE_PACK64)) { + gator_buffer_write_packed_int(cpu, COUNTER_BUF, 0); // key of zero indicates a timestamp + gator_buffer_write_packed_int64(cpu, COUNTER_BUF, gator_get_time()); + retval = true; + } + local_irq_restore(flags); + + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, COUNTER_BUF); + + return retval; +} + +static void marshal_event(int len, int* buffer) { + unsigned long i, flags, cpu = smp_processor_id(); + + if (len <= 0) + return; + + // length must be even since all data is a (key, value) pair + if (len & 0x1) { + pr_err("gator: invalid counter data detected and discarded"); + return; + } + + // events must be written in key,value pairs + for (i = 0; i < len; i += 2) { + local_irq_save(flags); + if (!buffer_check_space(cpu, COUNTER_BUF, MAXSIZE_PACK32 * 2)) { + local_irq_restore(flags); + break; + } + gator_buffer_write_packed_int(cpu, COUNTER_BUF, buffer[i]); + gator_buffer_write_packed_int(cpu, COUNTER_BUF, buffer[i + 1]); + local_irq_restore(flags); + } + + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, COUNTER_BUF); +} + +static void marshal_event64(int len, long long* buffer64) { + unsigned long i, flags, cpu = smp_processor_id(); + + if (len <= 0) + return; + + // length must be even since all data is a (key, value) pair + if (len & 0x1) { + pr_err("gator: invalid counter data detected and discarded"); + return; + } + + // events must be written in key,value pairs + for (i = 0; i < len; i += 2) { + local_irq_save(flags); + if (!buffer_check_space(cpu, COUNTER_BUF, MAXSIZE_PACK64 * 2)) { + local_irq_restore(flags); + break; + } + gator_buffer_write_packed_int64(cpu, COUNTER_BUF, buffer64[i]); + gator_buffer_write_packed_int64(cpu, COUNTER_BUF, buffer64[i + 1]); + local_irq_restore(flags); + } + + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, COUNTER_BUF); +} + +#if GATOR_CPU_FREQ_SUPPORT +static void marshal_event_single(int core, int key, int value) { + unsigned long flags, cpu; + + local_irq_save(flags); + cpu = smp_processor_id(); + if (buffer_check_space(cpu, COUNTER2_BUF, MAXSIZE_PACK64 + MAXSIZE_PACK32 * 3)) { + gator_buffer_write_packed_int64(cpu, COUNTER2_BUF, gator_get_time()); + gator_buffer_write_packed_int(cpu, COUNTER2_BUF, core); + gator_buffer_write_packed_int(cpu, COUNTER2_BUF, key); + gator_buffer_write_packed_int(cpu, COUNTER2_BUF, value); + } + local_irq_restore(flags); + + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, COUNTER2_BUF); +} +#endif + +static void marshal_sched_gpu(int type, int unit, int core, int tgid, int pid) { + unsigned long cpu = smp_processor_id(), flags; + + if (!per_cpu(gator_buffer, cpu)[GPU_TRACE_BUF]) + return; + + local_irq_save(flags); + if (buffer_check_space(cpu, GPU_TRACE_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32)) { + gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, type); + gator_buffer_write_packed_int64(cpu, GPU_TRACE_BUF, gator_get_time()); + gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, unit); + gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, core); + gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, tgid); + gator_buffer_write_packed_int(cpu, GPU_TRACE_BUF, pid); + } + local_irq_restore(flags); + + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, GPU_TRACE_BUF); +} + +static void marshal_sched_trace(int type, int pid, int tgid, int cookie, int state) { + unsigned long cpu = smp_processor_id(), flags; + + if (!per_cpu(gator_buffer, cpu)[SCHED_TRACE_BUF]) + return; + + local_irq_save(flags); + if (buffer_check_space(cpu, SCHED_TRACE_BUF, MAXSIZE_PACK64 + 5 * MAXSIZE_PACK32)) { + gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, type); + gator_buffer_write_packed_int64(cpu, SCHED_TRACE_BUF, gator_get_time()); + gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, pid); + gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, tgid); + gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, cookie); + gator_buffer_write_packed_int(cpu, SCHED_TRACE_BUF, state); + } + local_irq_restore(flags); + + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, SCHED_TRACE_BUF); +} + +#if GATOR_CPU_FREQ_SUPPORT +static void marshal_wfi(int core, int state) { + unsigned long flags, cpu; + + local_irq_save(flags); + cpu = smp_processor_id(); + if (buffer_check_space(cpu, WFI_BUF, MAXSIZE_PACK64 + MAXSIZE_PACK32 * 2)) { + gator_buffer_write_packed_int64(cpu, WFI_BUF, gator_get_time()); + gator_buffer_write_packed_int(cpu, WFI_BUF, core); + gator_buffer_write_packed_int(cpu, WFI_BUF, state); + } + local_irq_restore(flags); + + // Check and commit; commit is set to occur once buffer is 3/4 full + buffer_check(cpu, WFI_BUF); +} +#endif + +static void marshal_frame(int cpu, int buftype, int frame) { + // add response type + if (gator_response_type > 0) { + gator_buffer_write_packed_int(cpu, buftype, gator_response_type); + } + + // leave space for 4-byte unpacked length + per_cpu(gator_buffer_write, cpu)[buftype] = (per_cpu(gator_buffer_write, cpu)[buftype] + 4) & gator_buffer_mask[buftype]; + + // add frame type and core number + gator_buffer_write_packed_int(cpu, buftype, frame); + gator_buffer_write_packed_int(cpu, buftype, cpu); +} diff --git a/driver/gator_pack.c b/driver/gator_pack.c index 985e960..925469a 100644 --- a/driver/gator_pack.c +++ b/driver/gator_pack.c @@ -162,101 +162,3 @@ static void gator_buffer_write_packed_int64(int cpu, int buftype, unsigned long per_cpu(gator_buffer_write, cpu)[buftype] = write9; } } - -static int gator_write_packed_int(char *buffer, unsigned int x) -{ - if ((x & 0xffffff80) == 0) { - buffer[0] = x & 0x7f; - return 1; - } else if ((x & 0xffffc000) == 0) { - buffer[0] = x | 0x80; - buffer[1] = (x>>7) & 0x7f; - return 2; - } else if ((x & 0xffe00000) == 0) { - buffer[0] = x | 0x80; - buffer[1] = (x>>7) | 0x80; - buffer[2] = (x>>14) & 0x7f; - return 3; - } else if ((x & 0xf0000000) == 0) { - buffer[0] = x | 0x80; - buffer[1] = (x>>7) | 0x80; - buffer[2] = (x>>14) | 0x80; - buffer[3] = (x>>21) & 0x7f; - return 4; - } else { - buffer[0] = x | 0x80; - buffer[1] = (x>>7) | 0x80; - buffer[2] = (x>>14) | 0x80; - buffer[3] = (x>>21) | 0x80; - buffer[4] = (x>>28) & 0x0f; - return 5; - } -} - -static int gator_write_packed_int64(char *buffer, unsigned long long x) -{ - if ((x & 0xffffffffffffff80LL) == 0) { - buffer[0] = x & 0x7f; - return 1; - } else if ((x & 0xffffffffffffc000LL) == 0) { - buffer[0] = x | 0x80; - buffer[1] = (x>>7) & 0x7f; - return 2; - } else if ((x & 0xffffffffffe00000LL) == 0) { - buffer[0] = x | 0x80; - buffer[1] = (x>>7) | 0x80; - buffer[2] = (x>>14) & 0x7f; - return 3; - } else if ((x & 0xfffffffff0000000LL) == 0) { - buffer[0] = x | 0x80; - buffer[1] = (x>>7) | 0x80; - buffer[2] = (x>>14) | 0x80; - buffer[3] = (x>>21) & 0x7f; - return 4; - } else if ((x & 0xfffffff800000000LL) == 0) { - buffer[0] = x | 0x80; - buffer[1] = (x>>7) | 0x80; - buffer[2] = (x>>14) | 0x80; - buffer[3] = (x>>21) | 0x80; - buffer[4] = (x>>28) & 0x7f; - return 5; - } else if ((x & 0xfffffc0000000000LL) == 0) { - buffer[0] = x | 0x80; - buffer[1] = (x>>7) | 0x80; - buffer[2] = (x>>14) | 0x80; - buffer[3] = (x>>21) | 0x80; - buffer[4] = (x>>28) | 0x80; - buffer[5] = (x>>35) & 0x7f; - return 6; - } else if ((x & 0xfffe000000000000LL) == 0) { - buffer[0] = x | 0x80; - buffer[1] = (x>>7) | 0x80; - buffer[2] = (x>>14) | 0x80; - buffer[3] = (x>>21) | 0x80; - buffer[4] = (x>>28) | 0x80; - buffer[5] = (x>>35) | 0x80; - buffer[6] = (x>>42) & 0x7f; - return 7; - } else if ((x & 0xff00000000000000LL) == 0) { - buffer[0] = x | 0x80; - buffer[1] = (x>>7) | 0x80; - buffer[2] = (x>>14) | 0x80; - buffer[3] = (x>>21) | 0x80; - buffer[4] = (x>>28) | 0x80; - buffer[5] = (x>>35) | 0x80; - buffer[6] = (x>>42) | 0x80; - buffer[7] = (x>>49) & 0x7f; - return 8; - } else { - buffer[0] = x | 0x80; - buffer[1] = (x>>7) | 0x80; - buffer[2] = (x>>14) | 0x80; - buffer[3] = (x>>21) | 0x80; - buffer[4] = (x>>28) | 0x80; - buffer[5] = (x>>35) | 0x80; - buffer[6] = (x>>42) | 0x80; - buffer[7] = (x>>49) | 0x80; - buffer[8] = (x>>56) & 0xff; - return 9; - } -} diff --git a/driver/gator_trace_gpu.c b/driver/gator_trace_gpu.c index bc63995..921932c 100644 --- a/driver/gator_trace_gpu.c +++ b/driver/gator_trace_gpu.c @@ -25,68 +25,12 @@ static int mali_trace_registered; static int gpu_trace_registered; -#define GPU_OVERFLOW -1 #define GPU_START 1 #define GPU_STOP 2 #define GPU_UNIT_VP 1 #define GPU_UNIT_FP 2 - -#define TRACESIZE (8*1024) - -static DEFINE_PER_CPU(uint64_t *[2], theGpuTraceBuf); -static DEFINE_PER_CPU(int, theGpuTraceSel); -static DEFINE_PER_CPU(int, theGpuTracePos); -static DEFINE_PER_CPU(int, theGpuTraceErr); - -int gator_trace_gpu_read(long long **buffer); - -static void probe_gpu_write(int type, int unit, int core, struct task_struct* task) -{ - int tracePos; - unsigned long flags; - uint64_t *traceBuf, time; - int pid, tgid; - int cpu = smp_processor_id(); - - if (!per_cpu(theGpuTraceBuf, cpu)[per_cpu(theGpuTraceSel, cpu)]) - return; - - if (task) { - tgid = (int)task->tgid; - pid = (int)task->pid; - } else { - tgid = pid = 0; - } - - // disable interrupts to synchronize with gator_trace_gpu_read(); spinlocks not needed since percpu buffers are used - local_irq_save(flags); - - time = gator_get_time(); - tracePos = per_cpu(theGpuTracePos, cpu); - traceBuf = per_cpu(theGpuTraceBuf, cpu)[per_cpu(theGpuTraceSel, cpu)]; - - if (tracePos < (TRACESIZE - 100)) { - // capture - traceBuf[tracePos++] = type; - traceBuf[tracePos++] = time; - traceBuf[tracePos++] = unit; - traceBuf[tracePos++] = core; - traceBuf[tracePos++] = tgid; - traceBuf[tracePos++] = pid; - } else if (!per_cpu(theGpuTraceErr, cpu)) { - per_cpu(theGpuTraceErr, cpu) = 1; - traceBuf[tracePos++] = GPU_OVERFLOW; - traceBuf[tracePos++] = time; - traceBuf[tracePos++] = 0; - traceBuf[tracePos++] = 0; - traceBuf[tracePos++] = 0; - traceBuf[tracePos++] = 0; - pr_debug("gator: gpu trace overflow\n"); - } - per_cpu(theGpuTracePos, cpu) = tracePos; - local_irq_restore(flags); -} +#define GPU_UNIT_CL 3 #ifdef MALI_SUPPORT @@ -105,19 +49,23 @@ enum components { GATOR_DEFINE_PROBE(mali_timeline_event, TP_PROTO(unsigned int event_id, unsigned int d0, unsigned int d1, unsigned int d2, unsigned int d3, unsigned int d4)) { unsigned int component, state; + int tgid = 0, pid = 0; // do as much work as possible before disabling interrupts - component = (event_id >> 16) & 0xF; - state = (event_id >> 24) & 0xF; + component = (event_id >> 16) & 0xFF; // component is an 8-bit field + state = (event_id >> 24) & 0xF; // state is a 4-bit field if ((component == COMPONENT_VP0) || (component >= COMPONENT_FP0 && component <= COMPONENT_FP7)) { if (state == ACTIVITY_START || state == ACTIVITY_STOP) { unsigned int type = (state == ACTIVITY_START) ? GPU_START : GPU_STOP; unsigned int unit = (component < COMPONENT_FP0) ? GPU_UNIT_VP : GPU_UNIT_FP; unsigned int core = (component < COMPONENT_FP0) ? component - COMPONENT_VP0 : component - COMPONENT_FP0; - struct task_struct* task = (state == ACTIVITY_START) ? (struct task_struct*)d2 : NULL; + if (state == ACTIVITY_START) { + tgid = d0; + pid = d1; + } - probe_gpu_write(type, unit, core, task); + marshal_sched_gpu(type, unit, core, tgid, pid); } } } @@ -125,20 +73,18 @@ GATOR_DEFINE_PROBE(mali_timeline_event, TP_PROTO(unsigned int event_id, unsigned GATOR_DEFINE_PROBE(gpu_activity_start, TP_PROTO(int gpu_unit, int gpu_core, struct task_struct *p)) { - probe_gpu_write(GPU_START, gpu_unit, gpu_core, p); + marshal_sched_gpu(GPU_START, gpu_unit, gpu_core, (int)p->tgid, (int)p->pid); } GATOR_DEFINE_PROBE(gpu_activity_stop, TP_PROTO(int gpu_unit, int gpu_core)) { - probe_gpu_write(GPU_STOP, gpu_unit, gpu_core, NULL); + marshal_sched_gpu(GPU_STOP, gpu_unit, gpu_core, 0, 0); } int gator_trace_gpu_start(void) { - int cpu; - /* - * Returns 0 for installation failed + * Returns nonzero for installation failed * Absence of gpu trace points is not an error */ @@ -161,90 +107,20 @@ int gator_trace_gpu_start(void) gpu_trace_registered = 1; } - if (!gpu_trace_registered && !mali_trace_registered) { - return 0; - } - - for_each_present_cpu(cpu) { - per_cpu(theGpuTraceSel, cpu) = 0; - per_cpu(theGpuTracePos, cpu) = 0; - per_cpu(theGpuTraceErr, cpu) = 0; - per_cpu(theGpuTraceBuf, cpu)[0] = kmalloc(TRACESIZE * sizeof(uint64_t), GFP_KERNEL); - per_cpu(theGpuTraceBuf, cpu)[1] = kmalloc(TRACESIZE * sizeof(uint64_t), GFP_KERNEL); - if (!per_cpu(theGpuTraceBuf, cpu)[0] || !per_cpu(theGpuTraceBuf, cpu)[1]) { -#ifdef MALI_SUPPORT - if (mali_trace_registered) { - GATOR_UNREGISTER_TRACE(mali_timeline_event); - } -#endif - if (gpu_trace_registered) { - GATOR_UNREGISTER_TRACE(gpu_activity_stop); - GATOR_UNREGISTER_TRACE(gpu_activity_start); - } - - gpu_trace_registered = mali_trace_registered = 0; - - return -1; - } - } - return 0; } -int gator_trace_gpu_offline(long long **buffer) -{ - return gator_trace_gpu_read(buffer); -} - void gator_trace_gpu_stop(void) { - int cpu; - - if (gpu_trace_registered || mali_trace_registered) { - for_each_present_cpu(cpu) { - kfree(per_cpu(theGpuTraceBuf, cpu)[0]); - kfree(per_cpu(theGpuTraceBuf, cpu)[1]); - per_cpu(theGpuTraceBuf, cpu)[0] = NULL; - per_cpu(theGpuTraceBuf, cpu)[1] = NULL; - } - #ifdef MALI_SUPPORT - if (mali_trace_registered) { - GATOR_UNREGISTER_TRACE(mali_timeline_event); - } + if (mali_trace_registered) { + GATOR_UNREGISTER_TRACE(mali_timeline_event); + } #endif - if (gpu_trace_registered) { - GATOR_UNREGISTER_TRACE(gpu_activity_stop); - GATOR_UNREGISTER_TRACE(gpu_activity_start); - } - - gpu_trace_registered = mali_trace_registered = 0; - } -} - -int gator_trace_gpu_read(long long **buffer) -{ - int cpu = smp_processor_id(); - unsigned long flags; - uint64_t *traceBuf; - int tracePos; - - if (!per_cpu(theGpuTraceBuf, cpu)[per_cpu(theGpuTraceSel, cpu)]) - return 0; - - local_irq_save(flags); - - traceBuf = per_cpu(theGpuTraceBuf, cpu)[per_cpu(theGpuTraceSel, cpu)]; - tracePos = per_cpu(theGpuTracePos, cpu); - - per_cpu(theGpuTraceSel, cpu) = !per_cpu(theGpuTraceSel, cpu); - per_cpu(theGpuTracePos, cpu) = 0; - per_cpu(theGpuTraceErr, cpu) = 0; - - local_irq_restore(flags); - - if (buffer) - *buffer = traceBuf; + if (gpu_trace_registered) { + GATOR_UNREGISTER_TRACE(gpu_activity_stop); + GATOR_UNREGISTER_TRACE(gpu_activity_start); + } - return tracePos; + gpu_trace_registered = mali_trace_registered = 0; } diff --git a/driver/gator_trace_power.c b/driver/gator_trace_power.c new file mode 100755 index 0000000..ca89b19 --- /dev/null +++ b/driver/gator_trace_power.c @@ -0,0 +1,160 @@ +/** + * Copyright (C) ARM Limited 2011-2012. 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 +#include + +// cpu_frequency and cpu_idle trace points were introduced in Linux kernel v2.6.38 +// the now deprecated power_frequency trace point was available prior to 2.6.38, but only for x86 +#if GATOR_CPU_FREQ_SUPPORT +enum { + POWER_CPU_FREQ, + POWER_CPU_IDLE, + POWER_TOTAL +}; + +static DEFINE_PER_CPU(ulong, idle_prev_state); +static ulong power_cpu_enabled[POWER_TOTAL]; +static ulong power_cpu_key[POWER_TOTAL]; + +static int gator_trace_power_create_files(struct super_block *sb, struct dentry *root) +{ + struct dentry *dir; + + // cpu_frequency + dir = gatorfs_mkdir(sb, root, "Linux_power_cpu_freq"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &power_cpu_enabled[POWER_CPU_FREQ]); + gatorfs_create_ro_ulong(sb, dir, "key", &power_cpu_key[POWER_CPU_FREQ]); + + // cpu_idle + dir = gatorfs_mkdir(sb, root, "Linux_power_cpu_idle"); + if (!dir) { + return -1; + } + gatorfs_create_ulong(sb, dir, "enabled", &power_cpu_enabled[POWER_CPU_IDLE]); + gatorfs_create_ro_ulong(sb, dir, "key", &power_cpu_key[POWER_CPU_IDLE]); + + return 0; +} + +// 'cpu' may not equal smp_processor_id(), i.e. may not be running on the core that is having the freq/idle state change +GATOR_DEFINE_PROBE(cpu_frequency, TP_PROTO(unsigned int frequency, unsigned int cpu)) +{ + marshal_event_single(cpu, power_cpu_key[POWER_CPU_FREQ], frequency * 1000); +} + +#define WFI_ACTIVE_THRESHOLD 2 // may vary on platform/OS +#define WFI_EXIT 0 +#define WFI_ENTER 1 +GATOR_DEFINE_PROBE(cpu_idle, TP_PROTO(unsigned int state, unsigned int cpu)) +{ + // the streamline engine treats all counter values as unsigned + if (state & 0x80000000) { + state = 0; + } + + if (state == per_cpu(idle_prev_state, cpu)) { + return; + } + + if (state < WFI_ACTIVE_THRESHOLD && per_cpu(idle_prev_state, cpu) >= WFI_ACTIVE_THRESHOLD) { + // transition from wfi to non-wfi + marshal_wfi(cpu, WFI_EXIT); + } else if (state >= WFI_ACTIVE_THRESHOLD && per_cpu(idle_prev_state, cpu) < WFI_ACTIVE_THRESHOLD) { + // transition from non-wfi to wfi + marshal_wfi(cpu, WFI_ENTER); + } + + per_cpu(idle_prev_state, cpu) = state; + + if (power_cpu_enabled[POWER_CPU_IDLE]) { + marshal_event_single(cpu, power_cpu_key[POWER_CPU_IDLE], state); + } +} + +static void gator_trace_power_online(void) +{ + int cpu = smp_processor_id(); + if (power_cpu_enabled[POWER_CPU_FREQ]) { + marshal_event_single(cpu, power_cpu_key[POWER_CPU_FREQ], cpufreq_quick_get(cpu) * 1000); + } +} + +static void gator_trace_power_offline(void) +{ + // Set frequency to zero on an offline + int cpu = smp_processor_id(); + if (power_cpu_enabled[POWER_CPU_FREQ]) { + marshal_event_single(cpu, power_cpu_key[POWER_CPU_FREQ], 0); + } +} + +static int gator_trace_power_start(void) +{ + int cpu; + + // register tracepoints + if (power_cpu_enabled[POWER_CPU_FREQ]) + if (GATOR_REGISTER_TRACE(cpu_frequency)) + goto fail_cpu_frequency_exit; + + // Always register for cpu:idle for detecting WFI, independent of power_cpu_enabled[POWER_CPU_IDLE] + if (GATOR_REGISTER_TRACE(cpu_idle)) + goto fail_cpu_idle_exit; + pr_debug("gator: registered power event tracepoints\n"); + + for_each_present_cpu(cpu) { + per_cpu(idle_prev_state, cpu) = 0; + } + + return 0; + + // unregister tracepoints on error +fail_cpu_idle_exit: + if (power_cpu_enabled[POWER_CPU_FREQ]) + GATOR_UNREGISTER_TRACE(cpu_frequency); +fail_cpu_frequency_exit: + pr_err("gator: power event tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n"); + + return -1; +} + +static void gator_trace_power_stop(void) +{ + int i; + + if (power_cpu_enabled[POWER_CPU_FREQ]) + GATOR_UNREGISTER_TRACE(cpu_frequency); + GATOR_UNREGISTER_TRACE(cpu_idle); + pr_debug("gator: unregistered power event tracepoints\n"); + + for (i = 0; i < POWER_TOTAL; i++) { + power_cpu_enabled[i] = 0; + } +} + +void gator_trace_power_init(void) +{ + int i; + for (i = 0; i < POWER_TOTAL; i++) { + power_cpu_enabled[i] = 0; + power_cpu_key[i] = gator_events_get_key(); + } +} +#else +static int gator_trace_power_create_files(struct super_block *sb, struct dentry *root) {return 0;} +static void gator_trace_power_online(void) {} +static void gator_trace_power_offline(void) {} +static int gator_trace_power_start(void) {return 0;} +static void gator_trace_power_stop(void) {} +void gator_trace_power_init(void) {} +#endif diff --git a/driver/gator_trace_sched.c b/driver/gator_trace_sched.c index dafacb7..08b0270 100644 --- a/driver/gator_trace_sched.c +++ b/driver/gator_trace_sched.c @@ -10,34 +10,26 @@ #include #include "gator.h" -#define SCHED_OVERFLOW -1 #define SCHED_SWITCH 1 -#define SCHED_PROCESS_FREE 2 +#define SCHED_PROCESS_EXIT 2 -#define SCHEDSIZE (8*1024) #define TASK_MAP_ENTRIES 1024 /* must be power of 2 */ #define TASK_MAX_COLLISIONS 2 -static DEFINE_PER_CPU(uint64_t *[2], theSchedBuf); -static DEFINE_PER_CPU(int, theSchedSel); -static DEFINE_PER_CPU(int, theSchedPos); -static DEFINE_PER_CPU(int, theSchedErr); static DEFINE_PER_CPU(uint64_t *, taskname_keys); enum { - STATE_CONTENTION = 0, + STATE_WAIT_ON_OTHER = 0, + STATE_CONTENTION, STATE_WAIT_ON_IO, - STATE_WAIT_ON_OTHER + STATE_WAIT_ON_MUTEX, }; -int gator_trace_sched_read(long long **buffer); - void emit_pid_name(struct task_struct* task) { bool found = false; - unsigned long flags; char taskcomm[TASK_COMM_LEN + 3]; - int x, cpu = smp_processor_id(); + unsigned long x, cpu = smp_processor_id(); uint64_t *keys = &(per_cpu(taskname_keys, cpu)[(task->pid & 0xFF) * TASK_MAX_COLLISIONS]); uint64_t value; @@ -52,7 +44,7 @@ void emit_pid_name(struct task_struct* task) } } - if (!found && buffer_check_space(cpu, TIMER_BUF, TASK_COMM_LEN + 2 * MAXSIZE_PACK32 + MAXSIZE_PACK64)) { + if (!found) { // shift values, new value always in front uint64_t oldv, newv = value; for (x = 0; x < TASK_MAX_COLLISIONS; x++) { @@ -62,100 +54,70 @@ void emit_pid_name(struct task_struct* task) } // emit pid names, cannot use get_task_comm, as it's not exported on all kernel versions - if (strlcpy(taskcomm, task->comm, TASK_COMM_LEN) == TASK_COMM_LEN - 1) + if (strlcpy(taskcomm, task->comm, TASK_COMM_LEN) == TASK_COMM_LEN - 1) { // append ellipses if task->comm has length of TASK_COMM_LEN - 1 strcat(taskcomm, "..."); + } - // disable interrupts to synchronize with hrtimer populating timer buf - local_irq_save(flags); - gator_buffer_write_packed_int(cpu, TIMER_BUF, MESSAGE_PID_NAME); - gator_buffer_write_packed_int64(cpu, TIMER_BUF, gator_get_time()); - gator_buffer_write_packed_int(cpu, TIMER_BUF, task->pid); - gator_buffer_write_string(cpu, TIMER_BUF, taskcomm); - local_irq_restore(flags); + marshal_pid_name(task->pid, taskcomm); + } +} + + +static void collect_counters(void) +{ + int *buffer, len; + long long *buffer64; + struct gator_interface *gi; + + if (marshal_event_header()) { + list_for_each_entry(gi, &gator_events, list) { + if (gi->read) { + len = gi->read(&buffer); + marshal_event(len, buffer); + } else if (gi->read64) { + len = gi->read64(&buffer64); + marshal_event64(len, buffer64); + } + } } } static void probe_sched_write(int type, struct task_struct* task, struct task_struct* old_task) { - int schedPos, cookie = 0, state = 0; - unsigned long flags; - uint64_t *schedBuf, time; + int cookie = 0, state = 0; int cpu = smp_processor_id(); int pid = task->pid; int tgid = task->tgid; - if (!per_cpu(theSchedBuf, cpu)[per_cpu(theSchedSel, cpu)]) - return; - if (type == SCHED_SWITCH) { // do as much work as possible before disabling interrupts - cookie = get_exec_cookie(cpu, TIMER_BUF, task); + cookie = get_exec_cookie(cpu, BACKTRACE_BUF, task); emit_pid_name(task); - if (old_task->state == 0) + if (old_task->state == TASK_RUNNING) { state = STATE_CONTENTION; - else if (old_task->in_iowait) + } else if (old_task->in_iowait) { state = STATE_WAIT_ON_IO; - else +#ifdef CONFIG_DEBUG_MUTEXES + } else if (old_task->blocked_on) { + state = STATE_WAIT_ON_MUTEX; +#endif + } else { state = STATE_WAIT_ON_OTHER; - } - - // disable interrupts to synchronize with gator_trace_sched_read(); spinlocks not needed since percpu buffers are used - local_irq_save(flags); - - time = gator_get_time(); - schedPos = per_cpu(theSchedPos, cpu); - schedBuf = per_cpu(theSchedBuf, cpu)[per_cpu(theSchedSel, cpu)]; + } - if (schedPos < (SCHEDSIZE - 100)) { - // capture - schedBuf[schedPos++] = type; - schedBuf[schedPos++] = time; - schedBuf[schedPos++] = pid; - schedBuf[schedPos++] = tgid; - schedBuf[schedPos++] = cookie; - schedBuf[schedPos++] = state; - } else if (!per_cpu(theSchedErr, cpu)) { - per_cpu(theSchedErr, cpu) = 1; - schedBuf[schedPos++] = SCHED_OVERFLOW; - schedBuf[schedPos++] = time; - schedBuf[schedPos++] = 0; - schedBuf[schedPos++] = 0; - schedBuf[schedPos++] = 0; - schedBuf[schedPos++] = 0; - pr_debug("gator: tracepoint overflow\n"); + collect_counters(); } - per_cpu(theSchedPos, cpu) = schedPos; - local_irq_restore(flags); + + // marshal_sched_trace() disables interrupts as the free may trigger while switch is writing to the buffer; disabling preemption is not sufficient + // is disable interrupts necessary now that exit is used instead of free? + marshal_sched_trace(type, pid, tgid, cookie, state); } // special case used during a suspend of the system static void trace_sched_insert_idle(void) { - unsigned long flags; - uint64_t *schedBuf; - int schedPos, cpu = smp_processor_id(); - - if (!per_cpu(theSchedBuf, cpu)[per_cpu(theSchedSel, cpu)]) - return; - - local_irq_save(flags); - - schedPos = per_cpu(theSchedPos, cpu); - schedBuf = per_cpu(theSchedBuf, cpu)[per_cpu(theSchedSel, cpu)]; - - if (schedPos < (SCHEDSIZE - (6 * 8))) { - // capture - schedBuf[schedPos++] = SCHED_SWITCH; - schedBuf[schedPos++] = gator_get_time(); - schedBuf[schedPos++] = 0; // idle pid is zero - schedBuf[schedPos++] = 0; // idle tid is zero - schedBuf[schedPos++] = 0; // idle cookie is zero - schedBuf[schedPos++] = STATE_WAIT_ON_OTHER; - } - - per_cpu(theSchedPos, cpu) = schedPos; - local_irq_restore(flags); + marshal_sched_trace(SCHED_SWITCH, 0, 0, 0, 0); } #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) @@ -167,23 +129,23 @@ GATOR_DEFINE_PROBE(sched_switch, TP_PROTO(struct task_struct *prev, struct task_ probe_sched_write(SCHED_SWITCH, next, prev); } -GATOR_DEFINE_PROBE(sched_process_free, TP_PROTO(struct task_struct *p)) +GATOR_DEFINE_PROBE(sched_process_exit, TP_PROTO(struct task_struct *p)) { - probe_sched_write(SCHED_PROCESS_FREE, p, 0); + probe_sched_write(SCHED_PROCESS_EXIT, p, 0); } static int register_scheduler_tracepoints(void) { // register tracepoints if (GATOR_REGISTER_TRACE(sched_switch)) goto fail_sched_switch; - if (GATOR_REGISTER_TRACE(sched_process_free)) - goto fail_sched_process_free; + if (GATOR_REGISTER_TRACE(sched_process_exit)) + goto fail_sched_process_exit; pr_debug("gator: registered tracepoints\n"); return 0; // unregister tracepoints on error -fail_sched_process_free: +fail_sched_process_exit: GATOR_UNREGISTER_TRACE(sched_switch); fail_sched_switch: pr_err("gator: tracepoints failed to activate, please verify that tracepoints are enabled in the linux kernel\n"); @@ -196,14 +158,6 @@ int gator_trace_sched_start(void) int cpu, size; for_each_present_cpu(cpu) { - per_cpu(theSchedSel, cpu) = 0; - per_cpu(theSchedPos, cpu) = 0; - per_cpu(theSchedErr, cpu) = 0; - per_cpu(theSchedBuf, cpu)[0] = kmalloc(SCHEDSIZE * sizeof(uint64_t), GFP_KERNEL); - per_cpu(theSchedBuf, cpu)[1] = kmalloc(SCHEDSIZE * sizeof(uint64_t), GFP_KERNEL); - if (!per_cpu(theSchedBuf, cpu)) - return -1; - size = TASK_MAP_ENTRIES * TASK_MAX_COLLISIONS * sizeof(uint64_t); per_cpu(taskname_keys, cpu) = (uint64_t*)kmalloc(size, GFP_KERNEL); if (!per_cpu(taskname_keys, cpu)) @@ -214,16 +168,15 @@ int gator_trace_sched_start(void) return register_scheduler_tracepoints(); } -int gator_trace_sched_offline(long long **buffer) +void gator_trace_sched_offline(void) { trace_sched_insert_idle(); - return gator_trace_sched_read(buffer); } static void unregister_scheduler_tracepoints(void) { GATOR_UNREGISTER_TRACE(sched_switch); - GATOR_UNREGISTER_TRACE(sched_process_free); + GATOR_UNREGISTER_TRACE(sched_process_exit); pr_debug("gator: unregistered tracepoints\n"); } @@ -233,37 +186,6 @@ void gator_trace_sched_stop(void) unregister_scheduler_tracepoints(); for_each_present_cpu(cpu) { - kfree(per_cpu(theSchedBuf, cpu)[0]); - kfree(per_cpu(theSchedBuf, cpu)[1]); - per_cpu(theSchedBuf, cpu)[0] = NULL; - per_cpu(theSchedBuf, cpu)[1] = NULL; kfree(per_cpu(taskname_keys, cpu)); } } - -int gator_trace_sched_read(long long **buffer) -{ - int cpu = smp_processor_id(); - unsigned long flags; - uint64_t *schedBuf; - int schedPos; - - if (!per_cpu(theSchedBuf, cpu)[per_cpu(theSchedSel, cpu)]) - return 0; - - local_irq_save(flags); - - schedBuf = per_cpu(theSchedBuf, cpu)[per_cpu(theSchedSel, cpu)]; - schedPos = per_cpu(theSchedPos, cpu); - - per_cpu(theSchedSel, cpu) = !per_cpu(theSchedSel, cpu); - per_cpu(theSchedPos, cpu) = 0; - per_cpu(theSchedErr, cpu) = 0; - - local_irq_restore(flags); - - if (buffer) - *buffer = schedBuf; - - return schedPos; -} -- 2.39.2