summaryrefslogtreecommitdiffstats
blob: 5fad583f7bd095661d213d1f3d8f113b3bf63743 (plain) (blame)
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
/**
 * 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;
	}
}

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, const int groupFd) {
	if (fd == groupFd) {
		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;
		}

		// 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, groupFd) < 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;
}

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;
			const int offset = gSessionData->mLocalCapture ? 1 : 0;
			unsigned char header[7];
			header[0] = RESPONSE_APC_DATA;
			Buffer::writeLEInt(header + 1, head - tail + sizeof(header) - 5);
			// Should use real packing functions
			header[5] = FRAME_PERF;
			header[6] = cpu;

			// Write header
			sender->writeData(reinterpret_cast<const char *>(&header) + offset, sizeof(header) - offset, RESPONSE_APC_DATA);

			// Write data
			if ((head & ~BUF_MASK) == (tail & ~BUF_MASK)) {
				// Not wrapped
				sender->writeData(reinterpret_cast<const char *>(b + (tail & BUF_MASK)), head - tail, RESPONSE_APC_DATA);
			} else {
				// Wrapped
				sender->writeData(reinterpret_cast<const char *>(b + (tail & BUF_MASK)), BUF_SIZE - (tail & BUF_MASK), RESPONSE_APC_DATA);
				sender->writeData(reinterpret_cast<const char *>(b), head & BUF_MASK, RESPONSE_APC_DATA);
			}

			// 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;
			logg->logMessage("%s(%s:%i): Unmaped cpu %i", __FUNCTION__, __FILE__, __LINE__, cpu);
		}
	}

	return true;
}