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 src/monitor/SHMReader.cpp
10 #include <semaphore.h>
18 #include "monitor/SHMReader.h"
19 #include "config/GlobalVault.h"
20 #include "util/StreamAsString.h"
21 #include "util/MessageSystem.h"
22 #include "util/StringTo.h"
23 #include "shm/ZoneHeader.h"
27 SHMReader::ReadBroker::ReadBroker() : m_temporaryBuffer(NULL
), m_temporaryBufferSize(0) {
31 SHMReader::ReadBroker::~ReadBroker() {
32 if(m_temporaryBuffer
) delete[] m_temporaryBuffer
;
35 void SHMReader::ReadBroker::resizeBuffer(uint32_t newSize
) {
36 if(m_temporaryBufferSize
>= newSize
) return;
37 delete[] m_temporaryBuffer
;
38 m_temporaryBuffer
= new uint8_t[newSize
];
41 SHMReader::SHMReader() : m_header(NULL
) {
42 m_shmName
= Util::StreamAsString() << "/Aesalon-" << getpid();
44 m_fd
= shm_open(m_shmName
.c_str(), O_RDWR
| O_CREAT
, S_IRUSR
| S_IWUSR
);
45 if(m_fd
== -1) Message(Fatal
, "Could not open shared memory: " << strerror(errno
));
52 SHMReader::~SHMReader() {
56 uint32_t SHMReader::zoneCount() {
57 return m_header
->zoneCount
;
60 int32_t SHMReader::zoneWithData() {
61 for(uint32_t i
= 0; i
< m_header
->zonesAllocated
; i
++) {
62 if(m_zoneUseData
[i
/8] & (0x01 << (i
%8))) {
63 uint8_t *zone
= getZone(i
);
66 Message(Warning
, "Could not open zone #" << i
);
70 SHM::ZoneHeader
*zheader
= reinterpret_cast<SHM::ZoneHeader
*>(zone
);
72 if(sem_trywait(&zheader
->packetSemaphore
) == -1 && errno
== EAGAIN
) continue;
80 uint32_t SHMReader::zoneProcessID(uint32_t zoneID
) {
81 SHM::ZoneHeader
*header
= reinterpret_cast<SHM::ZoneHeader
*>(getZone(zoneID
));
82 return header
->processID
;
85 uint32_t SHMReader::zoneThreadID(uint32_t zoneID
) {
86 SHM::ZoneHeader
*header
= reinterpret_cast<SHM::ZoneHeader
*>(getZone(zoneID
));
87 return header
->threadID
;
90 void SHMReader::waitForPacket() {
91 sem_wait(&m_header
->packetSemaphore
);
94 void SHMReader::processRequest(ReadBroker
&request
) {
95 uint8_t *zone
= getZone(request
.zoneID());
97 SHM::ZoneHeader
*header
= reinterpret_cast<SHM::ZoneHeader
*>(zone
);
99 uint32_t start
= header
->head
;
100 uint32_t size1
= std::min(
102 (m_header
->zoneSize
*AesalonPageSize
- header
->gapSize
) - start
);
104 uint32_t size2
= request
.size() - size1
;
106 /* The difficult case; the data is in two segments. */
108 request
.resizeBuffer(request
.size());
109 memcpy(request
.temporaryBuffer(), zone
+ start
, size1
);
110 memcpy(request
.temporaryBuffer() + size1
, zone
+ ZoneDataOffset
, size2
);
111 request
.setData(request
.temporaryBuffer());
112 header
->head
= size2
+ ZoneDataOffset
;
113 if(header
->overflow
> 0) {
114 Message(Debug
, "overflow > 0 in difficult case, reducing . . .");
115 header
->overflow
-= size1
;
116 header
->overflow
-= header
->gapSize
;
117 header
->overflow
-= size2
;
119 if(header
->overflow
<= 0) {
120 Message(Debug
, "Posting to overflow semaphore . . .");
121 sem_post(&header
->overflowSemaphore
);
124 else header
->gapSize
= 0;
126 /* Otherwise, the simple case. */
128 request
.setData(zone
+ start
);
129 header
->head
+= size1
;
130 if(header
->overflow
> 0) {
131 Message(Debug
, "overflow > 0, reducing . . .");
132 header
->overflow
-= size1
;
133 Message(Debug
, "New overflow: " << header
->overflow
);
134 if(header
->overflow
<= 0) {
135 Message(Debug
, "Posting to overflow semaphore . . .");
136 sem_post(&header
->overflowSemaphore
);
142 uint8_t *SHMReader::getZone(uint32_t id
) {
143 if(id
< m_zoneList
.size() && m_zoneList
[id
] != NULL
) {
144 return m_zoneList
[id
];
148 static_cast<uint8_t *>(mapRegion(m_header
->zonePageOffset
+ id
*m_header
->zoneSize
, m_header
->zoneSize
));
150 m_zoneList
.resize(id
+1);
151 m_zoneList
[id
] = zone
;
156 void *SHMReader::mapRegion(uint32_t start
, uint32_t size
) {
157 if(m_header
== NULL
|| m_header
->shmSize
< (start
+size
)) {
158 if(m_header
) sem_wait(&m_header
->resizeSemaphore
);
160 Message(Debug
, "Resizing SHM to " << start
+size
<< " page(s).");
161 if(ftruncate(m_fd
, (start
+size
) * AesalonPageSize
) != 0) {
162 Message(Fatal
, "Could not resize shared memory.");
164 if(m_header
) m_header
->shmSize
= start
+size
;
166 if(m_header
) sem_post(&m_header
->resizeSemaphore
);
170 mmap(NULL
, size
* AesalonPageSize
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, m_fd
, start
* AesalonPageSize
);
175 void SHMReader::unmapRegion(void *data
, uint32_t size
) {
176 munmap(data
, size
*AesalonPageSize
);
179 void SHMReader::setupHeader() {
180 m_header
= reinterpret_cast<SHM::Header
*>(mapRegion(0, 1));
182 sem_init(&m_header
->packetSemaphore
, 1, 0);
183 sem_init(&m_header
->resizeSemaphore
, 1, 1);
186 void SHMReader::setupConfiguration() {
187 char *configurationData
= static_cast<char *>(mapRegion(1, 1));
188 m_header
->configDataSize
= 1;
190 std::vector
<Config::Vault::KeyPair
> configItems
;
191 Config::GlobalVault::instance()->match("*", configItems
);
195 for(std::vector
<Config::Vault::KeyPair
>::reverse_iterator i
= configItems
.rbegin(); i
!= configItems
.rend(); ++i
) {
196 /* Ignore all internal items. */
197 if(i
->first
.find("::") == 0) continue;
199 while((offset
+ i
->first
.length() + i
->second
.length() + 2) > m_header
->configDataSize
*AesalonPageSize
) {
200 unmapRegion(configurationData
, m_header
->configDataSize
);
201 m_header
->configDataSize
++;
202 configurationData
= static_cast<char *>(mapRegion(1, m_header
->configDataSize
));
204 memcpy(&configurationData
[offset
], i
->first
.c_str(), i
->first
.length()+1);
205 offset
+= i
->first
.length()+1;
206 memcpy(&configurationData
[offset
], i
->second
.c_str(), i
->second
.length()+1);
207 offset
+= i
->second
.length()+1;
211 void SHMReader::setupZones() {
212 m_header
->zoneCount
= 0;
213 m_header
->zoneSize
= Util::StringTo
<uint32_t>(Config::GlobalVault::instance()->get("monitor:zoneSize"));
214 m_header
->zoneUsagePages
= Util::StringTo
<uint32_t>(Config::GlobalVault::instance()->get("monitor:zoneUsePages"));
216 m_header
->zonePageOffset
= m_header
->configDataSize
+ 1 + m_header
->zoneUsagePages
;
218 m_zoneUseData
= static_cast<uint8_t *>(mapRegion(m_header
->configDataSize
+ 1, m_header
->zoneUsagePages
));
221 } // namespace Monitor