blob: f127c996d43b9c6559c1ad0fd25faec11017e2a6 [file] [log] [blame]
Jon Medhurst15ce78d2014-04-10 09:02:02 +01001/**
2 * Copyright (C) ARM Limited 2013-2014. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9#include "PerfBuffer.h"
10
11#include <sys/ioctl.h>
12#include <sys/mman.h>
13
14#include "Buffer.h"
15#include "Logging.h"
16#include "Sender.h"
17#include "SessionData.h"
18
19PerfBuffer::PerfBuffer() {
20 for (int cpu = 0; cpu < ARRAY_LENGTH(mBuf); ++cpu) {
21 mBuf[cpu] = MAP_FAILED;
22 mDiscard[cpu] = false;
Jon Medhurst96b56152014-10-30 18:01:15 +000023 mFds[cpu] = -1;
Jon Medhurst15ce78d2014-04-10 09:02:02 +010024 }
25}
26
27PerfBuffer::~PerfBuffer() {
28 for (int cpu = ARRAY_LENGTH(mBuf) - 1; cpu >= 0; --cpu) {
29 if (mBuf[cpu] != MAP_FAILED) {
30 munmap(mBuf[cpu], gSessionData->mPageSize + BUF_SIZE);
31 }
32 }
33}
34
Jon Medhurst96b56152014-10-30 18:01:15 +000035bool PerfBuffer::useFd(const int cpu, const int fd) {
36 if (mFds[cpu] < 0) {
Jon Medhurst15ce78d2014-04-10 09:02:02 +010037 if (mBuf[cpu] != MAP_FAILED) {
38 logg->logMessage("%s(%s:%i): cpu %i already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__, cpu);
39 return false;
40 }
41
42 // The buffer isn't mapped yet
43 mBuf[cpu] = mmap(NULL, gSessionData->mPageSize + BUF_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
44 if (mBuf[cpu] == MAP_FAILED) {
45 logg->logMessage("%s(%s:%i): mmap failed", __FUNCTION__, __FILE__, __LINE__);
46 return false;
47 }
Jon Medhurst96b56152014-10-30 18:01:15 +000048 mFds[cpu] = fd;
Jon Medhurst15ce78d2014-04-10 09:02:02 +010049
50 // Check the version
51 struct perf_event_mmap_page *pemp = static_cast<struct perf_event_mmap_page *>(mBuf[cpu]);
52 if (pemp->compat_version != 0) {
53 logg->logMessage("%s(%s:%i): Incompatible perf_event_mmap_page compat_version", __FUNCTION__, __FILE__, __LINE__);
54 return false;
55 }
56 } else {
57 if (mBuf[cpu] == MAP_FAILED) {
58 logg->logMessage("%s(%s:%i): cpu already online or not correctly cleaned up", __FUNCTION__, __FILE__, __LINE__);
59 return false;
60 }
61
Jon Medhurst96b56152014-10-30 18:01:15 +000062 if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, mFds[cpu]) < 0) {
Jon Medhurst15ce78d2014-04-10 09:02:02 +010063 logg->logMessage("%s(%s:%i): ioctl failed", __FUNCTION__, __FILE__, __LINE__);
64 return false;
65 }
66 }
67
68 return true;
69}
70
71void PerfBuffer::discard(const int cpu) {
72 if (mBuf[cpu] != MAP_FAILED) {
73 mDiscard[cpu] = true;
74 }
75}
76
77bool PerfBuffer::isEmpty() {
78 for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) {
79 if (mBuf[cpu] != MAP_FAILED) {
80 // Take a snapshot of the positions
81 struct perf_event_mmap_page *pemp = static_cast<struct perf_event_mmap_page *>(mBuf[cpu]);
82 const __u64 head = pemp->data_head;
83 const __u64 tail = pemp->data_tail;
84
85 if (head != tail) {
86 return false;
87 }
88 }
89 }
90
91 return true;
92}
93
Jon Medhurst96b56152014-10-30 18:01:15 +000094static void compressAndSend(const int cpu, const __u64 head, __u64 tail, const uint8_t *const b, Sender *const sender) {
95 // Pick a big size but something smaller than the chunkSize in Sender::writeData which is 100k
96 char buf[1<<16];
97 int writePos = 0;
98 const int typeLength = gSessionData->mLocalCapture ? 0 : 1;
99
100 while (head > tail) {
101 writePos = 0;
102 if (!gSessionData->mLocalCapture) {
103 buf[writePos++] = RESPONSE_APC_DATA;
104 }
105 // Reserve space for size
106 writePos += sizeof(uint32_t);
107 Buffer::packInt(buf, sizeof(buf), writePos, FRAME_PERF);
108 Buffer::packInt(buf, sizeof(buf), writePos, cpu);
109
110 while (head > tail) {
111 const int count = reinterpret_cast<const struct perf_event_header *>(b + (tail & BUF_MASK))->size/sizeof(uint64_t);
112 // Can this whole message be written as Streamline assumes events are not split between frames
113 if (sizeof(buf) <= writePos + count*Buffer::MAXSIZE_PACK64) {
114 break;
115 }
116 for (int i = 0; i < count; ++i) {
117 // Must account for message size
118 Buffer::packInt64(buf, sizeof(buf), writePos, *reinterpret_cast<const uint64_t *>(b + (tail & BUF_MASK)));
119 tail += sizeof(uint64_t);
120 }
121 }
122
123 // Write size
124 Buffer::writeLEInt(reinterpret_cast<unsigned char *>(buf + typeLength), writePos - typeLength - sizeof(uint32_t));
125 sender->writeData(buf, writePos, RESPONSE_APC_DATA);
126 }
127}
128
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100129bool PerfBuffer::send(Sender *const sender) {
130 for (int cpu = 0; cpu < gSessionData->mCores; ++cpu) {
131 if (mBuf[cpu] == MAP_FAILED) {
132 continue;
133 }
134
135 // Take a snapshot of the positions
136 struct perf_event_mmap_page *pemp = static_cast<struct perf_event_mmap_page *>(mBuf[cpu]);
137 const __u64 head = pemp->data_head;
138 const __u64 tail = pemp->data_tail;
139
140 if (head > tail) {
141 const uint8_t *const b = static_cast<uint8_t *>(mBuf[cpu]) + gSessionData->mPageSize;
Jon Medhurst96b56152014-10-30 18:01:15 +0000142 compressAndSend(cpu, head, tail, b, sender);
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100143
144 // Update tail with the data read
145 pemp->data_tail = head;
146 }
147
148 if (mDiscard[cpu]) {
149 munmap(mBuf[cpu], gSessionData->mPageSize + BUF_SIZE);
150 mBuf[cpu] = MAP_FAILED;
151 mDiscard[cpu] = false;
Jon Medhurst96b56152014-10-30 18:01:15 +0000152 mFds[cpu] = -1;
Jon Medhurst15ce78d2014-04-10 09:02:02 +0100153 logg->logMessage("%s(%s:%i): Unmaped cpu %i", __FUNCTION__, __FILE__, __LINE__, cpu);
154 }
155 }
156
157 return true;
158}