/** * Copyright (C) ARM Limited 2010-2014. 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 "FtraceSource.h" #include #include #include #include #include #include "DriverSource.h" #include "Logging.h" #include "SessionData.h" static void handler(int signum) { (void)signum; }; FtraceSource::FtraceSource(sem_t *senderSem) : mFtraceFh(NULL), mBuffer(0, FRAME_BLOCK_COUNTER, 128*1024, senderSem), mTid(-1), mTracingOn(0) { } FtraceSource::~FtraceSource() { } bool FtraceSource::prepare() { { struct sigaction act; act.sa_handler = handler; act.sa_flags = (int)SA_RESETHAND; if (sigaction(SIGUSR1, &act, NULL) != 0) { logg->logError(__FILE__, __LINE__, "sigaction failed: %s\n", strerror(errno)); handleException(); } } if (DriverSource::readIntDriver("/sys/kernel/debug/tracing/tracing_on", &mTracingOn)) { logg->logError(__FILE__, __LINE__, "Unable to read if ftrace is enabled"); handleException(); } if (DriverSource::writeDriver("/sys/kernel/debug/tracing/tracing_on", "0") != 0) { logg->logError(__FILE__, __LINE__, "Unable to turn ftrace off before truncating the buffer"); handleException(); } { int fd; fd = open("/sys/kernel/debug/tracing/trace", O_WRONLY | O_TRUNC | O_CLOEXEC, 0666); if (fd < 0) { logg->logError(__FILE__, __LINE__, "Unable truncate ftrace buffer: %s", strerror(errno)); handleException(); } close(fd); } if (DriverSource::writeDriver("/sys/kernel/debug/tracing/trace_clock", "perf") != 0) { logg->logError(__FILE__, __LINE__, "Unable to switch ftrace to the perf clock, please ensure you are running Linux 3.10 or later"); handleException(); } mFtraceFh = fopen_cloexec("/sys/kernel/debug/tracing/trace_pipe", "rb"); if (mFtraceFh == NULL) { logg->logError(__FILE__, __LINE__, "Unable to open trace_pipe"); handleException(); } return true; } void FtraceSource::run() { prctl(PR_SET_NAME, (unsigned long)&"gatord-ftrace", 0, 0, 0); mTid = syscall(__NR_gettid); if (DriverSource::writeDriver("/sys/kernel/debug/tracing/tracing_on", "1") != 0) { logg->logError(__FILE__, __LINE__, "Unable to turn ftrace on"); handleException(); } while (gSessionData->mSessionIsActive) { char buf[1<<12]; if (fgets(buf, sizeof(buf), mFtraceFh) == NULL) { if (errno == EINTR) { // Interrupted by interrupt - likely user request to terminate break; } logg->logError(__FILE__, __LINE__, "Unable read trace data: %s", strerror(errno)); handleException(); } const uint64_t currTime = getTime(); char *const colon = strstr(buf, ": "); if (colon == NULL) { logg->logError(__FILE__, __LINE__, "Unable find colon: %s", buf); handleException(); } *colon = '\0'; char *const space = strrchr(buf, ' '); if (space == NULL) { logg->logError(__FILE__, __LINE__, "Unable find space: %s", buf); handleException(); } *colon = ':'; int64_t *data = NULL; int count = gSessionData->ftraceDriver.read(colon + 2, &data); if (count > 0) { errno = 0; const long long time = strtod(space, NULL) * 1000000000; if (errno != 0) { logg->logError(__FILE__, __LINE__, "Unable to parse time: %s", strerror(errno)); handleException(); } mBuffer.event64(-1, time); for (int i = 0; i < count; ++i) { mBuffer.event64(data[2*i + 0], data[2*i + 1]); } mBuffer.check(currTime); } } mBuffer.setDone(); DriverSource::writeDriver("/sys/kernel/debug/tracing/tracing_on", mTracingOn); fclose(mFtraceFh); DriverSource::writeDriver("/sys/kernel/debug/tracing/trace_clock", "local"); } void FtraceSource::interrupt() { // Closing the underlying file handle does not result in the read on the ftrace file handle to return, so send a signal to the thread syscall(__NR_tgkill, getpid(), mTid, SIGUSR1); } bool FtraceSource::isDone() { return mBuffer.isDone(); } void FtraceSource::write(Sender *sender) { // Don't send ftrace data until the summary packet is sent so that monotonic delta is available if (!gSessionData->mSentSummary) { return; } if (!mBuffer.isDone()) { mBuffer.write(sender); } }