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
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
;
31 pthread_t monitorThreadList
[AesalonInformerMonitorThreadListSize
];
32 int monitorThreadListSize
;
34 pthread_t
*threadList
;
42 const char *configData
;
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
);
58 const char *shmName
= getenv("AesalonSHMName");
60 fprintf(stderr
, "AesalonSHMName not set, aborting . . .\n");
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
&& AI_InformerData
.shmHeader
->shmSize
< (start
+size
)) {
74 sem_wait(&AI_InformerData
.shmHeader
->resizeSemaphore
);
76 if(ftruncate(AI_InformerData
.shmFd
, (start
+size
) * AesalonPageSize
) != 0) {
77 fprintf(stderr
, "Could not resize shared memory.");
81 sem_post(&AI_InformerData
.shmHeader
->resizeSemaphore
);
85 mmap(NULL
, size
* AesalonPageSize
, PROT_READ
| PROT_WRITE
,
86 MAP_SHARED
, AI_InformerData
.shmFd
, start
* AesalonPageSize
);
91 static void *AI_SetupZone() {
92 /* While more memory is required . . . */
93 while(AI_InformerData
.shmHeader
->zoneCount
>= AI_InformerData
.shmHeader
->zonesAllocated
) {
94 /* Allocate more memory. */
95 sem_wait(&AI_InformerData
.shmHeader
->resizeSemaphore
);
97 /* This seemingly-nonsensical statement is to account for the fact that during the wait above,
98 another thread may have jumped in and allocated a zone.
100 if(AI_InformerData
.shmHeader
->zoneCount
>= AI_InformerData
.shmHeader
->zonesAllocated
) {
101 /* Allocate two zones per iteration. */
102 AI_InformerData
.shmHeader
->shmSize
+= AI_InformerData
.shmHeader
->zoneSize
* 2;
103 AI_InformerData
.shmHeader
->zonesAllocated
+= 2;
105 ftruncate(AI_InformerData
.shmFd
, AI_InformerData
.shmHeader
->shmSize
* AesalonPageSize
);
108 sem_post(&AI_InformerData
.shmHeader
->resizeSemaphore
);
112 for(i
= 0; i
< AI_InformerData
.shmHeader
->zonesAllocated
; i
++) {
113 if(AI_InformerData
.zoneUseData
[i
/8] & (0x01 << (i
% 8))) break;
115 if(i
== AI_InformerData
.shmHeader
->zonesAllocated
) {
116 /* Something went pretty seriously wrong. Perhaps another target jumped in and took the spot first? */
117 printf("Something very wrong occurred. Trying again . . .\n");
118 /* Just temporarily use recursion to fix the problem. Note: don't try this at home . . . */
121 AI_InformerData
.zoneUseData
[i
/8] |= (0x01 << (i
%8));
124 AI_InformerData
.shmHeader
->zonePageOffset
+ (i
*AI_InformerData
.shmHeader
->zoneSize
),
125 AI_InformerData
.shmHeader
->zoneSize
);
127 ((ZoneHeader
*)AI_Zone
)->head
= ((ZoneHeader
*)AI_Zone
)->tail
= ZoneDataOffset
;
128 ((ZoneHeader
*)AI_Zone
)->overflow
= 0;
129 /* TODO: implement support for finding the process ID. */
130 ((ZoneHeader
*)AI_Zone
)->processID
= 0;
131 ((ZoneHeader
*)AI_Zone
)->threadID
= ++AI_InformerData
.shmHeader
->lastThreadID
;
136 static void *AI_ReserveSpace(uint32_t size
) {
137 ZoneHeader
*header
= (ZoneHeader
*)AI_Zone
;
139 if(header
->head
<= header
->tail
) {
140 remaining
= ((AI_InformerData
.shmHeader
->zoneSize
*AesalonPageSize
) - ZoneDataOffset
)
141 - (header
->tail
- header
->head
);
144 remaining
= header
->head
- ZoneDataOffset
- header
->tail
;
147 if(remaining
< size
) {
148 header
->overflow
= size
- remaining
;
149 sem_wait(&header
->overflowSemaphore
);
152 /* If the head is less than (or equal to) the tail, then the used memory
153 is in one contiguous chunk, and the buffer has not wrapped yet. */
154 if(header
->head
<= header
->tail
) {
155 header
->tail
+= size
;
156 return &AI_Zone
[header
->tail
-size
];
159 printf("**** Second case, returning NULL.\n");
164 void __attribute__((constructor
)) AI_Construct() {
165 if(AI_InformerData
.initialized
== 1) return;
166 pthread_t self
= pthread_self();
167 AI_StopCollection(self
);
168 AI_InformerData
.initialized
= 1;
172 AI_InformerData
.threadList
= malloc(sizeof(pthread_t
) * 16);
173 AI_InformerData
.threadListSize
= 16;
174 AI_InformerData
.threadCount
= 1;
175 AI_InformerData
.threadList
[0] = self
;
177 AI_ContinueCollection(self
);
180 void __attribute__((destructor
)) AI_Destruct() {
184 void AC_EXPORT
AI_StartPacket(ModuleID moduleID
) {
185 if(AI_Zone
== NULL
) AI_SetupZone();
186 AI_ZonePacket
= AI_ReserveSpace(sizeof(PacketHeader
));
187 AI_ZonePacket
->packetSize
= 0;
188 AI_ZonePacket
->moduleID
= moduleID
;
192 void AC_EXPORT
*AI_PacketSpace(uint32_t size
) {
193 AI_ZonePacket
->packetSize
+= size
;
194 return AI_ReserveSpace(size
);
197 void AC_EXPORT
AI_EndPacket() {
201 const char *AI_ConfigurationString(const char *name
) {
204 const char *itemName
= &AI_InformerData
.configData
[offset
];
205 if(itemName
== 0 || itemName
[0] == 0) break;
207 int nameLength
= strlen(itemName
)+1;
208 const char *itemData
= &AI_InformerData
.configData
[offset
+nameLength
];
209 if(!strcmp(name
, itemName
)) return itemData
;
211 int dataLength
= strlen(itemData
)+1;
212 offset
+= nameLength
+ dataLength
;
217 long AI_ConfigurationLong(const char *name
) {
218 const char *s
= AI_ConfigurationString(name
);
219 if(s
== NULL
) return -1;
221 sscanf(s
, "%ld", &value
);
225 int AI_ConfigurationBool(const char *name
) {
226 const char *s
= AI_ConfigurationString(name
);
227 if(s
== NULL
) return 0;
228 return StringToBool(s
);
231 void AI_StopCollection(pthread_t threadID
) {
232 if(AI_InformerData
.monitorThreadListSize
< AesalonInformerMonitorThreadListSize
) {
233 AI_InformerData
.monitorThreadList
[AI_InformerData
.monitorThreadListSize
] = threadID
;
234 AI_InformerData
.monitorThreadListSize
++;
238 "Too many threads in monitor thread list, output data will be corrupted with Aesalon"
239 "collection-thread data.\n");
240 fprintf(stderr
, "Increasing threadListSize in build/config may be a good idea.\n");
244 void AI_ContinueCollection(pthread_t threadID
) {
246 while(i
< AI_InformerData
.monitorThreadListSize
) {
247 if(pthread_equal(AI_InformerData
.monitorThreadList
[i
], threadID
)) {
248 AI_InformerData
.monitorThreadListSize
--;
249 AI_InformerData
.monitorThreadList
[i
] =
250 AI_InformerData
.monitorThreadList
[AI_InformerData
.monitorThreadListSize
-1];
257 short AI_CollectionStatus() {
258 if(AI_InformerData
.initialized
== 0) return 0;
259 pthread_t self
= pthread_self();
261 while(i
< AI_InformerData
.monitorThreadListSize
) {
262 if(pthread_equal(self
, AI_InformerData
.monitorThreadList
[i
])) return 0;