Fixed enough bugs to allow the cpuTime module to properly create packets.
[aesalon.git] / modules / informer / src / collector / informer.c
blob5fdf2a313351bcecb2ea3a7082788a6bcc1cc5b0
1 /**
2 Aesalon, a tool to visualize a program's behaviour at run-time.
3 Copyright (C) 2010, Aesalon Development Team.
5 Aesalon is distributed under the terms of the GNU GPLv3. For more
6 licensing information, see the file LICENSE included with the distribution.
8 @file modules/informer/src/collector/informer.c
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <string.h>
16 #include <fcntl.h>
17 #include <sys/mman.h>
18 #include <sys/stat.h>
19 #include <sys/types.h>
20 #include <dlfcn.h>
21 #include <time.h>
22 #include <pthread.h>
23 #include <errno.h>
25 #include "informer/Informer.h"
26 #include "common/Config.h"
27 #include "common/ZoneHeader.h"
29 typedef struct Zone_t Zone_t;
30 struct Zone_t {
31 int offset;
32 Zone_t *next;
35 typedef struct InformerData_t InformerData_t;
36 struct InformerData_t {
37 int initialized;
38 uint64_t processID;
40 pthread_t monitorThreadList[AesalonInformerMonitorThreadListSize];
41 int monitorThreadListSize;
43 pthread_t *threadList;
44 int threadListSize;
45 int threadCount;
47 int shmFd;
49 SharedMemoryHeader_t *shmHeader;
51 const char *configData;
53 uint8_t *zoneUseData;
56 static InformerData_t AI_InformerData;
58 static __thread uint8_t *AI_Zone = NULL;
59 static __thread SHMPacketHeader_t *AI_ZonePacket = NULL;
61 /** Interally-used function; opens shared memory for later use.
62 @param name The name of the SHM to use.
64 static void AI_OpenSHM(const char *name);
66 static void AI_SetupHeader();
67 static void AI_SetupConfig();
68 static void AI_SetupZoneUse();
70 static void AI_SetupZone();
71 static void *AI_ReserveSpace(uint32_t amount);
73 static int AI_ZoneAvailable(uint32_t id);
74 static void AI_MarkZone(uint32_t id);
75 static void AI_ClearZone(uint32_t id);
77 /** Internally-used function to calculate the amount of space remaining in the zone for
78 the current thread.
79 @param zoneID The zone to calculate the remaining space.
81 static uint32_t AI_RemainingSpace();
83 /* ------------------------------------------------------------------ */
85 void __attribute__((constructor)) AI_Construct() {
86 /* By default, .initialized will be set to 0 (AI_InformerData is a global). */
87 if(AI_InformerData.initialized) return;
88 AI_InformerData.initialized = 1;
90 printf("[AI] **** Constructing Informer . . .\n");
92 pthread_t self = pthread_self();
94 AI_StopCollection(self);
96 pid_t pid = getpid();
98 char filename[1024];
100 int fd = open("/proc/self/cmdline", O_RDONLY);
102 read(fd, filename, sizeof(filename));
104 close(fd);
106 /* String hashing algorithm: djb2. */
108 uint64_t pathHash = 0;
109 int c = 0;
110 char *p = filename;
111 while((c = (*p++))) {
112 pathHash = c + (pathHash << 6) + (pathHash << 16) - pathHash;
115 /* Clear the first 16 bits for the PID to be inserted properly. */
116 pathHash &= ~0xffff;
118 AI_InformerData.processID = pathHash ^ pid;
120 const char *shmName = getenv("AesalonSHMName");
121 if(shmName == NULL) {
122 fprintf(stderr, "[aesalon] AesalonSHMName not set, aborting.\n");
123 exit(1);
125 AI_OpenSHM(shmName);
127 AI_SetupHeader();
128 AI_SetupConfig();
129 AI_SetupZoneUse();
131 AI_InformerData.threadList = malloc(sizeof(pthread_t) * 16);
132 AI_InformerData.threadListSize = 16;
133 AI_InformerData.threadCount = 1;
134 AI_InformerData.threadList[0] = self;
136 AI_ContinueCollection(self);
138 AI_StartPacket(0);
139 AI_PacketSpace(32);
140 AI_EndPacket();
143 void __attribute__((destructor)) AI_Destruct() {
144 printf("[AI] Destructing Informer . . .\n");
147 static void AI_OpenSHM(const char *name) {
148 AI_InformerData.shmFd = shm_open(name, O_RDWR, S_IRUSR | S_IWUSR);
151 static void AI_SetupHeader() {
152 AI_InformerData.shmHeader = mmap(NULL, AesalonPageSize,
153 PROT_READ | PROT_WRITE, MAP_SHARED, AI_InformerData.shmFd, 0);
156 static void AI_SetupConfig() {
157 AI_InformerData.configData = mmap(NULL, AI_InformerData.shmHeader->configDataSize*AesalonPageSize,
158 PROT_READ | PROT_WRITE, MAP_SHARED, AI_InformerData.shmFd, AesalonPageSize);
161 static void AI_SetupZoneUse() {
162 AI_InformerData.zoneUseData = mmap(NULL, AI_InformerData.shmHeader->zoneUsagePages*AesalonPageSize,
163 PROT_READ | PROT_WRITE, MAP_SHARED, AI_InformerData.shmFd,
164 (AI_InformerData.shmHeader->configDataSize + 1)*AesalonPageSize);
166 /* +1 for the header. */
167 AI_InformerData.shmHeader->zonePageOffset =
168 AI_InformerData.shmHeader->zoneUsagePages + AI_InformerData.shmHeader->configDataSize + 1;
171 static void AI_SetupZone() {
172 printf("Setting up new zone . . .\n");
173 printf("\tActive zone count: %i\n", AI_InformerData.shmHeader->zoneCount);
174 printf("\tAllocated zone count: %i\n", AI_InformerData.shmHeader->zonesAllocated);
175 /* Check if more memory is required. */
176 while(AI_InformerData.shmHeader->zoneCount >= AI_InformerData.shmHeader->zonesAllocated) {
177 /* Allocate more memory. */
178 sem_wait(&AI_InformerData.shmHeader->resizeSemaphore);
180 if(AI_InformerData.shmHeader->zoneCount >= AI_InformerData.shmHeader->zonesAllocated) {
181 AI_InformerData.shmHeader->shmSize += AI_InformerData.shmHeader->zoneSize;
183 AI_InformerData.shmHeader->zonesAllocated ++;
185 printf("New size: %i pages\n", AI_InformerData.shmHeader->shmSize);
186 ftruncate(AI_InformerData.shmFd, AI_InformerData.shmHeader->shmSize * AesalonPageSize);
189 sem_post(&AI_InformerData.shmHeader->resizeSemaphore);
192 uint32_t i;
193 for(i = 0; i < AI_InformerData.shmHeader->zonesAllocated; i ++) {
194 if(AI_ZoneAvailable(i)) break;
196 if(i == AI_InformerData.shmHeader->zonesAllocated) {
197 /* Something went pretty seriously wrong. Perhaps another target jumped in and took the spot first? */
198 printf("Something very wrong occurred. Trying again . . .\n");
199 AI_SetupZone();
201 printf("Marking zone . . .\n");
202 AI_MarkZone(i);
203 printf("Zone marked, mapping . . .\n");
204 AI_Zone = mmap(NULL,
205 AI_InformerData.shmHeader->zoneSize*AesalonPageSize,
206 PROT_READ | PROT_WRITE, MAP_SHARED, AI_InformerData.shmFd,
207 (AI_InformerData.shmHeader->zonePageOffset + i*AI_InformerData.shmHeader->zoneSize)*AesalonPageSize);
209 printf("Mapped, initializing . . .\n");
211 printf("Mapping offset: %x\n",
212 (AI_InformerData.shmHeader->zonePageOffset + i*AI_InformerData.shmHeader->zoneSize)*AesalonPageSize);
213 printf("Mapping size: %x\n", AI_InformerData.shmHeader->zoneSize*AesalonPageSize);
215 ((ZoneHeader_t *)AI_Zone)->head = ((ZoneHeader_t *)AI_Zone)->tail = ZoneDataOffset;
216 ((ZoneHeader_t *)AI_Zone)->overflow = 0;
217 ((ZoneHeader_t *)AI_Zone)->processID = getpid();
218 ((ZoneHeader_t *)AI_Zone)->threadID = pthread_self();
220 printf("Setting up semaphores . . .\n");
222 sem_init(&((ZoneHeader_t *)AI_Zone)->packetSemaphore, 1, 0);
223 sem_init(&((ZoneHeader_t *)AI_Zone)->overflowSemaphore, 1, 0);
225 printf("Set up zone properly . . .\n");
228 static int AI_ZoneAvailable(uint32_t id) {
229 uint32_t byteOffset = id / 8;
230 uint32_t bitOffset = id % 8;
231 uint32_t mask = 0x01;
232 return !(AI_InformerData.zoneUseData[byteOffset] & (mask << bitOffset));
235 static void AI_MarkZone(uint32_t id) {
236 uint32_t byteOffset = id / 8;
237 uint32_t bitOffset = id % 8;
238 uint32_t mask = 0x01;
239 AI_InformerData.zoneUseData[byteOffset] |= (mask << bitOffset);
240 AI_InformerData.shmHeader->zoneCount ++;
243 static void AI_ClearZone(uint32_t id) {
244 uint32_t byteOffset = id / 8;
245 uint32_t bitOffset = id % 8;
246 uint32_t mask = 0x01;
247 AI_InformerData.zoneUseData[byteOffset] &= ~(mask << bitOffset);
248 AI_InformerData.shmHeader->zoneCount --;
251 static uint32_t AI_RemainingSpace() {
252 ZoneHeader_t *header = (ZoneHeader_t *)AI_Zone;
253 if(header->head <= header->tail) {
254 return ((AI_InformerData.shmHeader->zoneSize*AesalonPageSize) - ZoneDataOffset)
255 - (header->tail - header->head);
257 else {
258 return header->head - ZoneDataOffset - header->tail;
262 static void *AI_ReserveSpace(uint32_t amount) {
263 uint32_t remaining = AI_RemainingSpace();
264 ZoneHeader_t *header = (ZoneHeader_t *)AI_Zone;
265 /*uint32_t zoneDataSize = (AI_InformerData.shmHeader->zoneSize*AesalonPageSize) - ZoneDataOffset;*/
266 if(remaining < amount) {
267 header->overflow = amount - remaining;
268 sem_wait(&header->overflowSemaphore);
271 /* If the head is less than (or equal to) the tail, then the used memory
272 is in one contiguous chunk, and the buffer has not wrapped yet. */
273 if(header->head <= header->tail) {
274 /* if(header->head + amount >= zoneDataSize) {
275 header->gapSize = (amount + header->head) - zoneDataSize;
276 amount += header->gapSize;
277 header->head = ZoneDataOffset;
280 header->tail += amount;
281 return &AI_Zone[header->tail-amount];
283 else {
284 printf("**** Second case, returning NULL.\n");
285 return NULL;
289 void AI_StartPacket(ModuleID moduleID) {
290 if(AI_Zone == NULL) AI_SetupZone();
291 AI_ZonePacket = AI_ReserveSpace(sizeof(SHMPacketHeader_t));
292 AI_ZonePacket->packetSize = 0;
293 AI_ZonePacket->moduleID = moduleID;
296 void AC_EXPORT *AI_PacketSpace(uint32_t size) {
297 AI_ZonePacket->packetSize += size;
298 return AI_ReserveSpace(size);
301 void AC_EXPORT AI_EndPacket() {
302 printf("packet size: %i\n", AI_ZonePacket->packetSize);
303 AI_ZonePacket = NULL;
305 ZoneHeader_t *header = (ZoneHeader_t *)AI_Zone;
307 sem_post(&header->packetSemaphore);
309 sem_post(&AI_InformerData.shmHeader->packetSemaphore);
312 uint64_t AI_Timestamp() {
313 struct timespec t;
314 clock_gettime(CLOCK_REALTIME, &t);
315 return (t.tv_sec * 1000000000) + t.tv_nsec;
318 const char *AI_ConfigurationString(const char *name) {
319 uint32_t offset = 0;
320 while(1) {
321 const char *itemName = &AI_InformerData.configData[offset];
322 if(itemName == 0 || itemName[0] == 0) break;
324 int nameLength = strlen(itemName)+1;
325 const char *itemData = &AI_InformerData.configData[offset+nameLength];
326 if(!strcmp(name, itemName)) return itemData;
328 int dataLength = strlen(itemData)+1;
329 offset += nameLength + dataLength;
331 return NULL;
334 long AI_ConfigurationLong(const char *name) {
335 const char *s = AI_ConfigurationString(name);
336 if(s == NULL) return -1;
337 long value;
338 sscanf(s, "%ld", &value);
339 return value;
342 int AI_ConfigurationBool(const char *name) {
343 const char *s = AI_ConfigurationString(name);
344 if(s == NULL) return 0;
345 return strcmp(s, "false") != 0;
348 pthread_t *AI_TargetThreadList(int *size) {
349 if(size == NULL) return NULL;
350 *size = AI_InformerData.threadCount;
352 return AI_InformerData.threadList;
355 short AI_CollectionStatus() {
356 if(AI_InformerData.threadList == NULL) return 0;
357 pthread_t self = pthread_self();
358 int i = 0;
359 while(i < AI_InformerData.monitorThreadListSize) {
360 if(pthread_equal(self, AI_InformerData.monitorThreadList[i])) return 0;
361 i ++;
364 return 1;
367 void AI_StopCollection(pthread_t tid) {
368 if(AI_InformerData.monitorThreadListSize < AesalonInformerMonitorThreadListSize) {
369 AI_InformerData.monitorThreadList[AI_InformerData.monitorThreadListSize] = tid;
370 AI_InformerData.monitorThreadListSize ++;
372 else {
373 fprintf(stderr,
374 "Too many threads in monitor thread list, output data will be corrupted with Aesalon"
375 "collection-thread data.\n");
376 fprintf(stderr, "Increasing threadListSize in build/config may be a good idea.\n");
380 void AI_ContinueCollection(pthread_t tid) {
381 int i = 0;
382 while(i < AI_InformerData.monitorThreadListSize) {
383 if(pthread_equal(AI_InformerData.monitorThreadList[i], tid)) {
384 AI_InformerData.monitorThreadListSize --;
385 AI_InformerData.monitorThreadList[i] =
386 AI_InformerData.monitorThreadList[AI_InformerData.monitorThreadListSize-1];
387 break;
389 i ++;