Fixed overflow support in SHM communication system.
[aesalon.git] / modules / informer / src / collector / Informer.c
blob142ee9e8e390afd214c6839e5a3179c9f1ca471c
1 /** Aesalon, a tool to visualize program behaviour in real time.
2 Copyright (C) 2009-2011, Aesalon development team.
4 Aesalon is distributed under the terms of the GNU GPLv3. See
5 the included file LICENSE for more information.
7 @file modules/informer/src/collector/Informer.c
8 */
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <sys/mman.h>
13 #include <sys/stat.h>
14 #include <fcntl.h>
15 #include <unistd.h>
16 #include <string.h>
17 #include <errno.h>
19 #include "Config.h"
21 #include "informer/Informer.h"
22 #include "shm/PacketHeader.h"
23 #include "shm/ZoneHeader.h"
24 #include "util/StringToBool.h"
26 typedef struct InformerData InformerData;
27 struct InformerData {
28 int initialized;
29 uint64_t processID;
31 pthread_t monitorThreadList[AesalonInformerMonitorThreadListSize];
32 int monitorThreadListSize;
34 pthread_t *threadList;
35 int threadListSize;
36 int threadCount;
38 int shmFd;
40 SHMHeader *shmHeader;
42 const char *configData;
44 uint8_t *zoneUseData;
47 static InformerData AI_InformerData;
49 static THREAD_SPECIFIC uint8_t *AI_Zone = NULL;
50 static THREAD_SPECIFIC PacketHeader *AI_ZonePacket = NULL;
52 static void AI_SetupSHM();
53 static void *AI_MapSHM(uint32_t start, uint32_t size);
54 static void *AI_SetupZone();
55 static void *AI_ReserveSpace(uint32_t size);
57 void AI_SetupSHM() {
58 const char *shmName = getenv("AesalonSHMName");
59 if(shmName == NULL) {
60 fprintf(stderr, "AesalonSHMName not set, aborting . . .\n");
61 exit(1);
64 AI_InformerData.shmFd = shm_open(shmName, O_RDWR, S_IRUSR | S_IWUSR);
66 AI_InformerData.shmHeader = AI_MapSHM(0, 1);
67 AI_InformerData.configData = AI_MapSHM(1, AI_InformerData.shmHeader->configDataSize);
68 AI_InformerData.zoneUseData =
69 AI_MapSHM(AI_InformerData.shmHeader->configDataSize+1, AI_InformerData.shmHeader->zoneUsagePages);
72 void *AI_MapSHM(uint32_t start, uint32_t size) {
73 if(AI_InformerData.shmHeader != NULL) printf("Overall size: %i\n", AI_InformerData.shmHeader->shmSize);
74 if(AI_InformerData.shmHeader != NULL && AI_InformerData.shmHeader->shmSize < (start+size)) {
75 sem_wait(&AI_InformerData.shmHeader->resizeSemaphore);
77 if(ftruncate(AI_InformerData.shmFd, (start+size) * AesalonPageSize) != 0) {
78 fprintf(stderr, "Could not resize shared memory.");
79 exit(1);
82 sem_post(&AI_InformerData.shmHeader->resizeSemaphore);
85 void *memory =
86 mmap(NULL, size * AesalonPageSize, PROT_READ | PROT_WRITE,
87 MAP_SHARED, AI_InformerData.shmFd, start * AesalonPageSize);
89 return memory;
92 static void *AI_SetupZone() {
93 /* While more memory is required . . . */
94 while(AI_InformerData.shmHeader->zoneCount >= AI_InformerData.shmHeader->zonesAllocated) {
95 /* Allocate more memory. */
96 sem_wait(&AI_InformerData.shmHeader->resizeSemaphore);
98 /* This seemingly-nonsensical statement is to account for the fact that during the wait above,
99 another thread may have jumped in and allocated a zone that can now be used.
101 if(AI_InformerData.shmHeader->zoneCount >= AI_InformerData.shmHeader->zonesAllocated) {
102 /* Allocate two zones per iteration. */
103 AI_InformerData.shmHeader->shmSize += AI_InformerData.shmHeader->zoneSize * 2;
104 AI_InformerData.shmHeader->zonesAllocated += 2;
106 ftruncate(AI_InformerData.shmFd, AI_InformerData.shmHeader->shmSize * AesalonPageSize);
109 sem_post(&AI_InformerData.shmHeader->resizeSemaphore);
112 printf("Allocated zones: %i\n", AI_InformerData.shmHeader->zonesAllocated);
114 uint32_t i;
115 for(i = 0; i < AI_InformerData.shmHeader->zonesAllocated; i ++) {
116 if((AI_InformerData.zoneUseData[i/8] & (0x01 << (i % 8))) == 0) break;
118 printf("Zone ID#: %i\n", i);
119 if(i == AI_InformerData.shmHeader->zonesAllocated) {
120 /* Something went pretty seriously wrong. Perhaps another target jumped in and took the spot first? */
121 printf("Something very wrong occurred. Trying again . . .\n");
122 /* Just temporarily use recursion to fix the problem. Note: don't try this at home . . . */
123 AI_SetupZone();
125 AI_InformerData.zoneUseData[i/8] |= (0x01 << (i%8));
127 AI_Zone = AI_MapSHM(
128 AI_InformerData.shmHeader->zonePageOffset + (i*AI_InformerData.shmHeader->zoneSize),
129 AI_InformerData.shmHeader->zoneSize);
131 ((ZoneHeader *)AI_Zone)->head = ((ZoneHeader *)AI_Zone)->tail = ZoneDataOffset;
132 ((ZoneHeader *)AI_Zone)->overflow = 0;
133 /* TODO: implement support for finding the process ID. */
134 ((ZoneHeader *)AI_Zone)->processID = 0;
135 ((ZoneHeader *)AI_Zone)->threadID = ++AI_InformerData.shmHeader->lastThreadID;
137 return AI_Zone;
140 static void *AI_ReserveSpace(uint32_t size) {
141 ZoneHeader *header = (ZoneHeader *)AI_Zone;
142 uint32_t remaining;
143 /* Three cases for remaining check (and also below).
144 1) head > tail (memory is in two chunks)
145 2) head < tail (memory is in one chunk), tail+size >= zoneSize
146 3) head < tail (memory is in one chunk), tail+size < zoneSize
148 if(header->head > header->tail) {
149 remaining = header->head - ZoneDataOffset - header->tail;
151 else if(header->head <= header->tail
152 && (header->tail + size) < AI_InformerData.shmHeader->zoneSize*AesalonPageSize) {
154 remaining = AI_InformerData.shmHeader->zoneSize*AesalonPageSize
155 - (header->tail - header->head) - ZoneDataOffset;
157 else if(header->head <= header->tail
158 && (header->tail + size) >= AI_InformerData.shmHeader->zoneSize*AesalonPageSize) {
160 remaining = header->head - ZoneDataOffset;
163 if(remaining < size) {
164 printf("Not enough space remaining!\n");
165 printf("remaining: %i\n", remaining);
166 header->overflow = size - remaining;
167 printf("Overflow: %i\n", header->overflow);
168 sem_wait(&header->overflowSemaphore);
171 if(header->head > header->tail) {
172 header->tail += size;
173 return AI_Zone + header->tail - size;
175 else if(header->head <= header->tail
176 && (header->tail + size) < AI_InformerData.shmHeader->zoneSize*AesalonPageSize) {
178 header->tail += size;
179 return AI_Zone + header->tail - size;
181 else if(header->head <= header->tail
182 && (header->tail + size) >= AI_InformerData.shmHeader->zoneSize*AesalonPageSize) {
184 header->gapSize = AI_InformerData.shmHeader->zoneSize*AesalonPageSize - header->tail;
185 header->tail = ZoneDataOffset + size;
186 return AI_Zone + ZoneDataOffset;
189 printf("Something unforeseen occured. This should not have happened . . .\n");
190 return NULL;
193 void __attribute__((constructor)) AI_Construct() {
194 if(AI_InformerData.initialized == 1) return;
195 pthread_t self = pthread_self();
196 AI_StopCollection(self);
197 AI_InformerData.initialized = 1;
199 AI_SetupSHM();
201 AI_InformerData.threadList = malloc(sizeof(pthread_t) * 16);
202 AI_InformerData.threadListSize = 16;
203 AI_InformerData.threadCount = 1;
204 AI_InformerData.threadList[0] = self;
206 AI_ContinueCollection(self);
208 ModuleID id = 0;
209 for(; ; id ++) {
210 AI_StartPacket(id);
211 AI_PacketSpace(16);
212 AI_EndPacket();
216 void __attribute__((destructor)) AI_Destruct() {
220 void AC_EXPORT AI_StartPacket(ModuleID moduleID) {
221 if(AI_Zone == NULL) AI_SetupZone();
222 AI_ZonePacket = AI_ReserveSpace(sizeof(PacketHeader));
223 AI_ZonePacket->packetSize = 0;
224 AI_ZonePacket->moduleID = moduleID;
227 void AC_EXPORT *AI_PacketSpace(uint32_t size) {
228 AI_ZonePacket->packetSize += size;
229 return AI_ReserveSpace(size);
232 void AC_EXPORT AI_EndPacket() {
233 AI_ZonePacket = NULL;
234 sem_post(&((ZoneHeader *)AI_Zone)->packetSemaphore);
235 sem_post(&AI_InformerData.shmHeader->packetSemaphore);
238 const char *AI_ConfigurationString(const char *name) {
239 uint32_t offset = 0;
240 while(1) {
241 const char *itemName = &AI_InformerData.configData[offset];
242 if(itemName == 0 || itemName[0] == 0) break;
244 int nameLength = strlen(itemName)+1;
245 const char *itemData = &AI_InformerData.configData[offset+nameLength];
246 if(!strcmp(name, itemName)) return itemData;
248 int dataLength = strlen(itemData)+1;
249 offset += nameLength + dataLength;
251 return NULL;
254 long AI_ConfigurationLong(const char *name) {
255 const char *s = AI_ConfigurationString(name);
256 if(s == NULL) return -1;
257 long value;
258 sscanf(s, "%ld", &value);
259 return value;
262 int AI_ConfigurationBool(const char *name) {
263 const char *s = AI_ConfigurationString(name);
264 if(s == NULL) return 0;
265 return StringToBool(s);
268 void AI_StopCollection(pthread_t threadID) {
269 if(AI_InformerData.monitorThreadListSize < AesalonInformerMonitorThreadListSize) {
270 AI_InformerData.monitorThreadList[AI_InformerData.monitorThreadListSize] = threadID;
271 AI_InformerData.monitorThreadListSize ++;
273 else {
274 fprintf(stderr,
275 "Too many threads in monitor thread list, output data will be corrupted with Aesalon"
276 "collection-thread data.\n");
277 fprintf(stderr, "Increasing threadListSize in build/config may be a good idea.\n");
281 void AI_ContinueCollection(pthread_t threadID) {
282 int i = 0;
283 while(i < AI_InformerData.monitorThreadListSize) {
284 if(pthread_equal(AI_InformerData.monitorThreadList[i], threadID)) {
285 AI_InformerData.monitorThreadListSize --;
286 AI_InformerData.monitorThreadList[i] =
287 AI_InformerData.monitorThreadList[AI_InformerData.monitorThreadListSize-1];
288 break;
290 i ++;
294 short AI_CollectionStatus() {
295 if(AI_InformerData.initialized == 0) return 0;
296 pthread_t self = pthread_self();
297 int i = 0;
298 while(i < AI_InformerData.monitorThreadListSize) {
299 if(pthread_equal(self, AI_InformerData.monitorThreadList[i])) return 0;
300 i ++;
303 return 1;