Skip to content

Commit c45bdbe

Browse files
[REFACTOR] Introduce QNX Single Thread Logging
1. Pthread APIs in QNX follow POSIX standard, hence, to avoid undefined behaviors: - Return value from each API call will be handled - Manually allocate stack size for thread - Reduce thread queue to one for logging only to avoid unlimited threads creation - Use only 1 thread to handle logging, with stack size fixed to 12K (4K aligned) 2. Injection callback: - Since only 1 thread is used, callback injection will now do infinity wait inside slogger2_callback 3. Handle properly dlt thread in normal exit: - Add an atomic_bool for thread cancellation in normal main thread exit case. 4. Handle slog2_thread heap memory: - slog2_thread is now allocated on HEAP, in case of failure or normal exit, free this HEAP allocated memory is required to prevent memory leak. During thread allocation on heap, the pointer stackaddr shall be round up to the nearest aligned memory address within 16K memory allocated. Example: Heap 16K memory |-------------|------------------------| 0x1003 0x2000-aligned MAX_MEM MAX_MEM - 0x1003 = 16K MAX_MEM - 0x2000 >= 12K Signed-off-by: LUU QUANG MINH <Minh.LuuQuang@vn.bosch.com>
1 parent 46b58d4 commit c45bdbe

File tree

7 files changed

+224
-151
lines changed

7 files changed

+224
-151
lines changed

doc/dlt-qnx-system.md

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,33 @@ The application listens to the system logs on QNX (slogger2) and sends them to
77
DLT. The prerequisite is that dlt-daemon is already started and running.
88
dlt-qnx-system loads by default the configuration file ```/etc/dlt-qnx-system.conf```.
99

10-
> In order to catch all logs via slog2, the syslog needs to forward to slogger2 in [syslog.conf](http://www.qnx.com/developers/docs/7.0.0/index.html#com.qnx.doc.neutrino.utilities/topic/s/syslog.conf.html).
10+
> In order to catch all logs via slog2, the syslog needs to forward to slogger2 in [syslog.conf](http://www.qnx.com/developers/docs/7.0.0/index.html#com.qnx.doc.neutrino.utilities/topic/s/syslog.conf.html). [Deprecated]
1111
1212
To change the log level, use application ID and context ID set in configuration
1313
file. ```DLT_INITIAL_LOG_LEVEL``` can be set on startup phase, or control
1414
message can be sent from DLT client (e.g. dlt-control, DLT Viewer) at runtime.
1515
Refer to [dlt_for_developers.md](dlt_for_developers.md) for more detail.
1616

17+
## Single Threaded Design (v2.18.10+)
18+
19+
> **Note:** Starting from version 2.18.10, `dlt-qnx-system` uses a single-threaded design for QNX logging management.
20+
21+
- The main process initializes and manages the logging thread for the slogger2 adapter.
22+
- Signal handling is centralized in the main thread, ensuring clean shutdown and resource release.
23+
- The slogger2 adapter thread is started with retries and monitored for successful startup.
24+
- Runtime enable/disable of the slog2 adapter via injection is still supported.
25+
26+
The design shall improve reliability and simplifies the process structure, making it more suitable for embedded and high-load QNX systems.
27+
28+
### Architecture Comparison
29+
30+
**Original Design:**
31+
32+
<img src="images/dlt_qnx_system_original_design.png" alt="Original Design" width="700px" />
33+
34+
**New Single Threaded Design:**
35+
36+
<img src="images/dlt_qnx_system_new_design.png" alt="Original Design" width="700px" />
1737

1838
## Usage
1939

@@ -101,7 +121,6 @@ The description field is in the registration with libdlt.
101121
}
102122
```
103123

104-
105124
### Runtime enable/disable slog2 adapter
106125

107126
The slog2 parser callback can be disabled and reenabled at runtime by using an injection.
195 KB
Loading
312 KB
Loading

src/dlt-qnx-system/dlt-qnx-slogger2-adapter.cpp

Lines changed: 119 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
#include <sys/slog2.h>
2929
#include <sys/json.h>
3030
#include <slog2_parse.h>
31-
#include <thread>
31+
#include <atomic>
3232
#include <set>
3333

3434
#include "dlt-qnx-system.h"
@@ -43,17 +43,29 @@ inline int32_t logToDlt(DltContextData &log, const json_decoder_error_t &value)
4343
return logToDlt(log, static_cast<int>(value));
4444
}
4545

46+
std::atomic<bool> g_inj_disable_slog2_cb(false);
47+
std::atomic<bool> g_slog2_thread_alive(false);
48+
4649
extern DltContext dltQnxSystem;
4750

4851
static DltContext dltQnxSlogger2Context;
52+
4953
static std::set<std::string> dltWarnedMissingMappings;
5054

5155
extern DltQnxSystemThreads g_threads;
5256

53-
extern volatile bool g_inj_disable_slog2_cb;
54-
5557
static std::unordered_map<std::string, DltContext*> g_slog2file;
5658

59+
static void *stackaddr;
60+
61+
void free_stackaddr()
62+
{
63+
if (stackaddr) {
64+
free(stackaddr);
65+
stackaddr = NULL;
66+
}
67+
}
68+
5769
static void dlt_context_map_read(const char *json_filename)
5870
{
5971
DLT_LOG_CXX(dltQnxSlogger2Context, DLT_LOG_VERBOSE,
@@ -95,15 +107,13 @@ static void dlt_context_map_read(const char *json_filename)
95107
break;
96108
}
97109

98-
auto ctxt = new DltContext;
99-
g_slog2file.emplace(name, ctxt);
100-
101110
auto search = g_slog2file.find(name);
102111
if (search == g_slog2file.end()) {
103-
DLT_LOG_CXX(dltQnxSlogger2Context, DLT_LOG_INFO,
104-
"Could not emplace slog2ctxt map key: ", name);
105-
} else {
112+
auto ctxt = new DltContext;
113+
g_slog2file.emplace(name, ctxt);
106114
dlt_register_context(ctxt, ctxtID, description);
115+
} else {
116+
dlt_register_context(search->second, ctxtID, description);
107117
}
108118

109119
ret = json_decoder_pop(dec);
@@ -177,18 +187,28 @@ static bool wait_for_buffer_space(const double max_usage_threshold,
177187
* Function which is invoked by slog2_parse_all()
178188
* See slog2_parse_all api docs on qnx.com for details
179189
*/
180-
static int sloggerinfo_callback(slog2_packet_info_t *info, void *payload, void *param)
190+
static int slogger2_callback(slog2_packet_info_t *info, void *payload, void *param)
181191
{
182192
DltQnxSystemConfiguration* conf = (DltQnxSystemConfiguration*) param;
183193

184-
if (param == NULL)
194+
/* Normal exit from main thread during working */
195+
if (!g_slog2_thread_alive) {
185196
return -1;
197+
}
186198

187-
if (g_inj_disable_slog2_cb == true) {
199+
if (g_inj_disable_slog2_cb) {
200+
do {
201+
DLT_LOG(dltQnxSystem, DLT_LOG_INFO,
202+
DLT_STRING("Disabling slog2 callback because of injection request."));
203+
sleep(1);
204+
/* Unexpected exit when hanging */
205+
if (!g_slog2_thread_alive) {
206+
return -1;
207+
}
208+
} while (g_inj_disable_slog2_cb);
188209
DLT_LOG(dltQnxSystem, DLT_LOG_INFO,
189-
DLT_STRING("Disabling slog2 callback by injection request."));
190-
return -1;
191-
}
210+
DLT_STRING("Enabling slog2 callback because of injection request."));
211+
};
192212

193213
DltLogLevelType loglevel;
194214
switch (info->severity)
@@ -261,11 +281,16 @@ static int sloggerinfo_callback(slog2_packet_info_t *info, void *payload, void *
261281

262282
static void *slogger2_thread(void *v_conf)
263283
{
264-
int ret = -1;
265284
DltQnxSystemConfiguration *conf = (DltQnxSystemConfiguration *)v_conf;
266285

267-
if (v_conf == NULL)
268-
return reinterpret_cast<void*>(EINVAL);
286+
if (conf == NULL) {
287+
DLT_LOG_CXX(dltQnxSystem, DLT_LOG_DEBUG, __func__, ": Invalid config data.");
288+
DLT_UNREGISTER_CONTEXT(dltQnxSlogger2Context);
289+
/* Try to send SIGTERM to make sure main thread wakes up for cleaning */
290+
pthread_kill(g_threads.main_thread, SIGTERM);
291+
pthread_exit(NULL);
292+
return NULL;
293+
}
269294

270295
slog2_packet_info_t packet_info = SLOG2_PACKET_INFO_INIT;
271296

@@ -276,39 +301,67 @@ static void *slogger2_thread(void *v_conf)
276301
* Thread will block inside this function to get new log because
277302
* flag = SLOG2_PARSE_FLAGS_DYNAMIC
278303
*/
279-
ret = slog2_parse_all(
280-
SLOG2_PARSE_FLAGS_DYNAMIC, /* live streaming of all buffers merged */
281-
NULL, NULL, &packet_info, sloggerinfo_callback, (void*) conf);
282-
283-
if (ret == -1) {
284-
DLT_LOG_CXX(dltQnxSlogger2Context, DLT_LOG_ERROR,
285-
"slog2_parse_all() returned error=", ret);
286-
ret = EBADMSG;
304+
if (slog2_parse_all(SLOG2_PARSE_FLAGS_DYNAMIC, NULL, NULL,
305+
&packet_info, slogger2_callback, (void*) conf) == -1) {
306+
DLT_LOG_CXX(dltQnxSlogger2Context, DLT_LOG_WARN,
307+
"slog2_parse_all() stops working.\n");
287308
}
288309

289310
DLT_LOG_CXX(dltQnxSystem, DLT_LOG_DEBUG, __func__, ": Exited main loop.");
290311

291312
DLT_UNREGISTER_CONTEXT(dltQnxSlogger2Context);
292-
293-
/* process should be shutdown if the callback was not manually disabled */
294-
if (g_inj_disable_slog2_cb == false) {
295-
for (auto& x: g_slog2file) {
296-
if(x.second != NULL) {
297-
delete(x.second);
298-
x.second = NULL;
299-
}
300-
}
301-
/* Send a signal to main thread to wake up sigwait */
302-
pthread_kill(g_threads.mainThread, SIGTERM);
303-
}
304-
305-
return reinterpret_cast<void*>(ret);
313+
/* Try to send SIGTERM to make sure main thread wakes up for cleaning */
314+
pthread_kill(g_threads.main_thread, SIGTERM);
315+
pthread_exit(NULL);
316+
return NULL;
306317
}
307318

308319
void start_qnx_slogger2(DltQnxSystemConfiguration *conf)
309320
{
310-
static pthread_attr_t t_attr;
311-
static pthread_t pt;
321+
if (conf == NULL) {
322+
printf("Error in setup local database. No thread created.\n");
323+
return;
324+
}
325+
326+
int ret;
327+
pthread_attr_t thread_attr;
328+
void *aligned_stackaddr = NULL;
329+
size_t stacksize = PTHREAD_STACK_4K * 3;
330+
331+
ret = pthread_attr_init(&thread_attr);
332+
if (ret != 0) {
333+
printf("pthread_attr_init returned: %d. Error: %d\n", ret, errno);
334+
return;
335+
}
336+
337+
/* Get a big enough stack and align it on 4K boundary. */
338+
stackaddr = malloc(PTHREAD_STACK_4K * 4);
339+
if (stackaddr != NULL) {
340+
aligned_stackaddr = (void *)((((uintptr_t)stackaddr + (PTHREAD_STACK_4K - 1)) /
341+
PTHREAD_STACK_4K) * PTHREAD_STACK_4K);
342+
/* Example: stackaddr = 0x1003 (not aligned), boundary: 4K (4096)
343+
* Round up to nearest aligned: 0x1003 + 0x0fff = 0x2002
344+
* Round down to integer portion: 0x2002 / 4096 = 2
345+
* aligned 4K mem: 2 * 0x1000 = 0x2000
346+
* In fact, size = 12K < 16K, so the new aligned_stackaddr will be allocated
347+
* within, e.g. 0x1003 and max heap address of malloc 16K -> Safe here
348+
*/
349+
printf("Using PTHREAD_STACK_4K to align. Set stackaddr to aligned address %p and stacksize to %zu\n", aligned_stackaddr, stacksize);
350+
} else {
351+
printf("Unable to allocate stack memory.\n");
352+
pthread_attr_destroy(&thread_attr);
353+
return;
354+
}
355+
356+
ret = pthread_attr_setstack(&thread_attr, aligned_stackaddr, stacksize);
357+
if (ret != 0) {
358+
free_stackaddr();
359+
pthread_attr_destroy(&thread_attr);
360+
printf("pthread_attr_setstack returned: %d. Error: %d\n", ret, errno);
361+
return;
362+
} else {
363+
printf("Successfully set stackaddr and stacksize.\n");
364+
}
312365

313366
DLT_REGISTER_CONTEXT(dltQnxSlogger2Context, conf->qnxslogger2.contextId,
314367
"SLOGGER2 Adapter");
@@ -318,12 +371,30 @@ void start_qnx_slogger2(DltQnxSystemConfiguration *conf)
318371
DLT_LOG_CXX(dltQnxSlogger2Context, DLT_LOG_DEBUG,
319372
"dlt-qnx-slogger2-adapter, start syslog");
320373

321-
pthread_attr_init(&t_attr);
322-
323-
const int ret = pthread_create(&pt, &t_attr, slogger2_thread, conf);
324-
if (0 != ret) {
325-
DLT_LOG_CXX(dltQnxSlogger2Context, DLT_LOG_ERROR, __func__, "pthread_create failed with error, ret=", ret);
374+
ret = pthread_create(&g_threads.slog2_thread, &thread_attr, slogger2_thread, conf);
375+
if (ret != 0) {
376+
pthread_attr_destroy(&thread_attr);
377+
clean_qnx_slogger2();
378+
fprintf(stderr, "Failed to create thread: %d %s\n", ret, strerror(ret));
379+
return;
326380
} else {
327-
g_threads.threads[g_threads.count++] = pt;
381+
g_slog2_thread_alive = true;
382+
}
383+
384+
ret = pthread_attr_destroy(&thread_attr);
385+
if (ret != 0) {
386+
printf("Error in pthread_attr_destroy. Returned: %d, Error: %d\n", ret, errno);
387+
return;
388+
}
389+
}
390+
391+
void clean_qnx_slogger2()
392+
{
393+
free_stackaddr();
394+
for (auto& x: g_slog2file) {
395+
if(x.second != NULL) {
396+
delete(x.second);
397+
x.second = NULL;
398+
}
328399
}
329400
}

0 commit comments

Comments
 (0)