PerfBuffer.cpp 4.57 KB
Newer Older
Abhijith PA's avatar
Abhijith PA committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
/**
 * Copyright (C) ARM Limited 2013-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 "PerfBuffer.h"

#include <sys/ioctl.h>
#include <sys/mman.h>

#include "Buffer.h"
#include "Logging.h"
#include "Sender.h"
#include "SessionData.h"

PerfBuffer::PerfBuffer() {
	for (int cpu = 0; cpu < ARRAY_LENGTH(mBuf); ++cpu) {
		mBuf[cpu] = MAP_FAILED;
		mDiscard[cpu] = false;
		mFds[cpu] = -1;
	}
}

PerfBuffer::~PerfBuffer() {
	for (int cpu = ARRAY_LENGTH(mBuf) - 1; cpu >= 0; --cpu) {
		if (mBuf[cpu] != MAP_FAILED) {
			munmap(mBuf[cpu], gSessionData->mPageSize + BUF_SIZE);
		}
	}
}

bool PerfBuffer::useFd(const int cpu, const int fd) {
	if (mFds[cpu] < 0) {
		if (mBuf[cpu] != MAP_FAILED) {
			logg->logMessage("%s(%s:%i): cpu %i already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__, cpu);
			return false;
		}

		// The buffer isn't mapped yet
		mBuf[cpu] = mmap(NULL, gSessionData->mPageSize + BUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
		if (mBuf[cpu] == MAP_FAILED) {
			logg->logMessage("%s(%s:%i): mmap failed", __FUNCTION__, __FILE__, __LINE__);
			return false;
		}
		mFds[cpu] = fd;

		// Check the version
		struct perf_event_mmap_page *pemp = static_cast<struct perf_event_mmap_page *>(mBuf[cpu]);
		if (pemp->compat_version != 0) {
			logg->logMessage("%s(%s:%i): Incompatible perf_event_mmap_page compat_version", __FUNCTION__, __FILE__, __LINE__);
			return false;
		}
	} else {
		if (mBuf[cpu] == MAP_FAILED) {
			logg->logMessage("%s(%s:%i): cpu already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__);
			return false;
		}

		if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, mFds[cpu]) < 0) {
			logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__);
			return false;
		}
	}

	return true;
}

void PerfBuffer::discard(const int cpu) {
	if (mBuf[cpu] != MAP_FAILED) {
		mDiscard[cpu] = true;
	}
}

bool PerfBuffer::isEmpty() {
	for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) {
		if (mBuf[cpu] != MAP_FAILED) {
			// Take a snapshot of the positions
			struct perf_event_mmap_page *pemp = static_cast<struct perf_event_mmap_page *>(mBuf[cpu]);
			const __u64 head = pemp->data_head;
			const __u64 tail = pemp->data_tail;

			if (head != tail) {
				return false;
			}
		}
	}

	return true;
}

static void compressAndSend(const int cpu, const __u64 head, __u64 tail, const uint8_t *const b, Sender *const sender) {
	// Pick a big size but something smaller than the chunkSize in Sender::writeData which is 100k
	char buf[1<<16];
	int writePos = 0;
	const int typeLength = gSessionData->mLocalCapture ? 0 : 1;

	while (head > tail) {
		writePos = 0;
		if (!gSessionData->mLocalCapture) {
			buf[writePos++] = RESPONSE_APC_DATA;
		}
		// Reserve space for size
		writePos += sizeof(uint32_t);
		Buffer::packInt(buf, sizeof(buf), writePos, FRAME_PERF);
		Buffer::packInt(buf, sizeof(buf), writePos, cpu);

		while (head > tail) {
			const int count = reinterpret_cast<const struct perf_event_header *>(b + (tail & BUF_MASK))->size/sizeof(uint64_t);
			// Can this whole message be written as Streamline assumes events are not split between frames
			if (sizeof(buf) <= writePos + count*Buffer::MAXSIZE_PACK64) {
				break;
			}
			for (int i = 0; i < count; ++i) {
				// Must account for message size
				Buffer::packInt64(buf, sizeof(buf), writePos, *reinterpret_cast<const uint64_t *>(b + (tail & BUF_MASK)));
				tail += sizeof(uint64_t);
			}
		}

		// Write size
		Buffer::writeLEInt(reinterpret_cast<unsigned char *>(buf + typeLength), writePos - typeLength - sizeof(uint32_t));
		sender->writeData(buf, writePos, RESPONSE_APC_DATA);
	}
}

bool PerfBuffer::send(Sender *const sender) {
	for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) {
		if (mBuf[cpu] == MAP_FAILED) {
			continue;
		}

		// Take a snapshot of the positions
		struct perf_event_mmap_page *pemp = static_cast<struct perf_event_mmap_page *>(mBuf[cpu]);
		const __u64 head = pemp->data_head;
		const __u64 tail = pemp->data_tail;

		if (head > tail) {
			const uint8_t *const b = static_cast<uint8_t *>(mBuf[cpu]) + gSessionData->mPageSize;
			compressAndSend(cpu, head, tail, b, sender);

			// Update tail with the data read
			pemp->data_tail = head;
		}

		if (mDiscard[cpu]) {
			munmap(mBuf[cpu], gSessionData->mPageSize + BUF_SIZE);
			mBuf[cpu] = MAP_FAILED;
			mDiscard[cpu] = false;
			mFds[cpu] = -1;
			logg->logMessage("%s(%s:%i): Unmaped cpu %i", __FUNCTION__, __FILE__, __LINE__, cpu);
		}
	}

	return true;
}