/** * Copyright (C) ARM Limited 2011-2016. 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 "StreamlineSetup.h" #include "Buffer.h" #include "CapturedXML.h" #include "ConfigurationXML.h" #include "Driver.h" #include "EventsXML.h" #include "Logging.h" #include "OlySocket.h" #include "OlyUtility.h" #include "Sender.h" #include "SessionData.h" static const char TAG_SESSION[] = "session"; static const char TAG_REQUEST[] = "request"; static const char TAG_CONFIGURATIONS[] = "configurations"; static const char ATTR_TYPE[] = "type"; static const char VALUE_EVENTS[] = "events"; static const char VALUE_CONFIGURATION[] = "configuration"; static const char VALUE_COUNTERS[] = "counters"; static const char VALUE_CAPTURED[] = "captured"; static const char VALUE_DEFAULTS[] = "defaults"; StreamlineSetup::StreamlineSetup(OlySocket* s) { bool ready = false; char* data = NULL; int type; mSocket = s; // Receive commands from Streamline (master) while (!ready) { // receive command over socket gSessionData.mWaitingOnCommand = true; data = readCommand(&type); // parse and handle data switch (type) { case COMMAND_REQUEST_XML: handleRequest(data); break; case COMMAND_DELIVER_XML: handleDeliver(data); break; case COMMAND_APC_START: logg.logMessage("Received apc start request"); ready = true; break; case COMMAND_APC_STOP: logg.logMessage("Received apc stop request before apc start request"); exit(0); break; case COMMAND_DISCONNECT: logg.logMessage("Received disconnect command"); exit(0); break; case COMMAND_PING: logg.logMessage("Received ping command"); sendData(NULL, 0, RESPONSE_ACK); break; default: logg.logError("Target error: Unknown command type, %d", type); handleException(); } free(data); } if (gSessionData.mCountersError != NULL) { logg.logError("%s", gSessionData.mCountersError); handleException(); } } StreamlineSetup::~StreamlineSetup() { } char* StreamlineSetup::readCommand(int* command) { unsigned char header[5]; char* data; int response; // receive type and length response = mSocket->receiveNBytes((char*)&header, sizeof(header)); // After receiving a single byte, we are no longer waiting on a command gSessionData.mWaitingOnCommand = false; if (response < 0) { logg.logError("Target error: Unexpected socket disconnect"); handleException(); } const char type = header[0]; const int length = (header[1] << 0) | (header[2] << 8) | (header[3] << 16) | (header[4] << 24); // add artificial limit if ((length < 0) || length > 1024 * 1024) { logg.logError("Target error: Invalid length received, %d", length); handleException(); } // allocate memory to contain the xml file, size of zero returns a zero size object data = (char*)calloc(length + 1, 1); if (data == NULL) { logg.logError("Unable to allocate memory for xml"); handleException(); } // receive data response = mSocket->receiveNBytes(data, length); if (response < 0) { logg.logError("Target error: Unexpected socket disconnect"); handleException(); } // null terminate the data for string parsing if (length > 0) { data[length] = 0; } *command = type; return data; } void StreamlineSetup::handleRequest(char* xml) { mxml_node_t *tree, *node; const char * attr = NULL; tree = mxmlLoadString(NULL, xml, MXML_NO_CALLBACK); node = mxmlFindElement(tree, tree, TAG_REQUEST, ATTR_TYPE, NULL, MXML_DESCEND_FIRST); if (node) { attr = mxmlElementGetAttr(node, ATTR_TYPE); } if (attr && strcmp(attr, VALUE_EVENTS) == 0) { sendEvents(); logg.logMessage("Sent events xml response"); } else if (attr && strcmp(attr, VALUE_CONFIGURATION) == 0) { sendConfiguration(); logg.logMessage("Sent configuration xml response"); } else if (attr && strcmp(attr, VALUE_COUNTERS) == 0) { sendCounters(); logg.logMessage("Sent counters xml response"); } else if (attr && strcmp(attr, VALUE_CAPTURED) == 0) { CapturedXML capturedXML; char* capturedText = capturedXML.getXML(false); sendData(capturedText, strlen(capturedText), RESPONSE_XML); free(capturedText); logg.logMessage("Sent captured xml response"); } else if (attr && strcmp(attr, VALUE_DEFAULTS) == 0) { sendDefaults(); logg.logMessage("Sent default configuration xml response"); } else { char error[] = "Unknown request"; sendData(error, strlen(error), RESPONSE_NAK); logg.logMessage("Received unknown request:\n%s", xml); } mxmlDelete(tree); } void StreamlineSetup::handleDeliver(char* xml) { mxml_node_t *tree; // Determine xml type tree = mxmlLoadString(NULL, xml, MXML_NO_CALLBACK); if (mxmlFindElement(tree, tree, TAG_SESSION, NULL, NULL, MXML_DESCEND_FIRST)) { // Session XML gSessionData.parseSessionXML(xml); 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); } mxmlDelete(tree); } void StreamlineSetup::sendData(const char* data, uint32_t length, char type) { unsigned char header[5]; header[0] = type; Buffer::writeLEInt(header + 1, length); mSocket->send((char*)&header, sizeof(header)); mSocket->send(data, length); } void StreamlineSetup::sendEvents() { EventsXML eventsXML; char* string = eventsXML.getXML(); sendString(string, RESPONSE_XML); free(string); } void StreamlineSetup::sendConfiguration() { ConfigurationXML xml; const char* string = xml.getConfigurationXML(); sendData(string, strlen(string), RESPONSE_XML); } void StreamlineSetup::sendDefaults() { // Send the config built into the binary char* xml = ConfigurationXML::getDefaultConfigurationXml(); size_t size = strlen(xml); // Artificial size restriction if (size > 1024*1024) { logg.logError("Corrupt default configuration file"); handleException(); } sendData(xml, size, RESPONSE_XML); free(xml); } void StreamlineSetup::sendCounters() { mxml_node_t *xml; mxml_node_t *counters; xml = mxmlNewXML("1.0"); counters = mxmlNewElement(xml, "counters"); int count = 0; for (Driver *driver = Driver::getHead(); driver != NULL; driver = driver->getNext()) { count += driver->writeCounters(counters); } if (count == 0) { logg.logError("No counters found, this could be because /dev/gator/events can not be read or because perf is not working correctly"); handleException(); } mxml_node_t *setup = mxmlNewElement(counters, "setup_warnings"); mxmlNewText(setup, 0, logg.getSetup()); if (gSessionData.mSharedData->mClustersAccurate) { for (int cluster = 0; cluster < gSessionData.mSharedData->mClusterCount; ++cluster) { mxml_node_t *node = mxmlNewElement(counters, "cluster"); mxmlElementSetAttrf(node, "id", "%i", cluster); mxmlElementSetAttr(node, "name", gSessionData.mSharedData->mClusters[cluster]->getPmncName()); } for (int cpu = 0; cpu < gSessionData.mCores; ++cpu) { if (gSessionData.mSharedData->mClusterIds[cpu] >= 0) { mxml_node_t *node = mxmlNewElement(counters, "cpu"); mxmlElementSetAttrf(node, "id", "%i", cpu); mxmlElementSetAttrf(node, "cluster", "%i", gSessionData.mSharedData->mClusterIds[cpu]); } } } char* string = mxmlSaveAllocString(xml, mxmlWhitespaceCB); mxmlDelete(xml); sendString(string, RESPONSE_XML); free(string); } void StreamlineSetup::writeConfiguration(char* xml) { char path[PATH_MAX]; ConfigurationXML::getPath(path); if (writeToDisk(path, xml) < 0) { logg.logError("Error writing %s\nPlease verify write permissions to this path.", path); handleException(); } // Re-populate gSessionData with the configuration, as it has now changed { ConfigurationXML configuration; } if (gSessionData.mCountersError != NULL) { logg.logError("%s", gSessionData.mCountersError); handleException(); } }