Skip to content

Commit 9d57831

Browse files
committed
add logging mechanism
fixes: #247 Signed-off-by: Łukasz Plewa <lukasz.plewa@intel.com>
1 parent 0cfa3bb commit 9d57831

File tree

11 files changed

+750
-5
lines changed

11 files changed

+750
-5
lines changed

scripts/docs_config/introduction.rst

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,45 @@ the `allocation API`_ as a first argument. There is also a possibility to
9595
retrieve a memory pool from an existing memory pointer that points to a memory
9696
previously allocated by UMF.
9797

98+
Logging
99+
---------------------
100+
101+
Logging in UMF is handled by logger. There are several levels of logging: *debug*, *info*, *warning*, and *error*.
102+
The level of logging determines what messages will be printed, ie. the level set to *warning* means all messages at levels *warning* and *error* will be printed.
103+
104+
By default, there is a guarantee that *error* messages are flushed immediately. One can change this behavior to flush on lower-level messages.
105+
106+
Loggers redirect messages to *stdout*, *stderr*, or a file
107+
108+
By default, no messages are printed. To enable logger you have to set **UMF_LOG** environment variable which have following syntax for setting logger options:
109+
110+
"[level:debug|info|warning|error];[flush:debug|info|warning|error];[output:stdout|stderr|file,<path>];[timestamp:yes|no];[pid:yes|no]"
111+
112+
* level - a log level, meaning that only messages from this level and above are printed.
113+
Possible values, from the lowest level to the highest one: *debug*, *info*, *warning*, *error*,
114+
* flush - a flush level, meaning that messages at this level and above are guaranteed to be flushed immediately,
115+
possible values are the same as above,
116+
* output - indicates where messages should be printed.
117+
Possible values are: *stdout*, *stderr* and *file*,
118+
when providing a *file* output option, a *<path>* is required
119+
* timestamp - add a timestamp to log message
120+
* pid - add thread and process ids to log message
121+
122+
.. note::
123+
For output to file, a path to the file has to be provided after a comma, like in the example above. The path has to exist, file will be created if not existing. Path to the file should be no longer than 255 characters long.
124+
The output parameter is required, all other logger options are optional. The defaults are set when options are not provided in the environment variable.
125+
Options have to be separated with `;`, option names and their values with `:`. Additionally, when providing *file* output, the keyword *file* and a path to a file
126+
have to be separated by `'`.
127+
128+
An example of an environment variable for setting up logger with logging level set to *info*, flush level set to *warning*, and output set to
129+
the ``out.log`` file::
130+
131+
UMF_LOG="level:info;flush:warning;output:file,out.log"
132+
133+
An example of an environment variable for setting up logger with logging level set to *warning* and output set to stdout::
134+
135+
UMF_LOGL="level:warning;output:stdout"
136+
98137
.. _UMF: https://github.com/oneapi-src/unified-memory-framework
99138
.. _README.md: https://github.com/oneapi-src/unified-memory-framework/blob/main/README.md
100139
.. _allocation API: https://oneapi-src.github.io/unified-memory-framework/api.html#memory-pool

src/libumf_linux.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212
#include "base_alloc_global.h"
1313
#include "memspace_host_all_internal.h"
1414
#include "provider_tracking.h"
15+
#include "utils_log.h"
1516

1617
umf_memory_tracker_handle_t TRACKER = NULL;
1718

1819
void __attribute__((constructor)) umfCreate(void) {
20+
util_log_init();
1921
TRACKER = umfMemoryTrackerCreate();
2022
}
2123

src/libumf_windows.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,14 @@
1212

1313
#include "base_alloc_global.h"
1414
#include "provider_tracking.h"
15+
#include "utils_log.h"
1516

1617
umf_memory_tracker_handle_t TRACKER = NULL;
1718

18-
static void umfCreate(void) { TRACKER = umfMemoryTrackerCreate(); }
19+
static void umfCreate(void) {
20+
util_log_init();
21+
TRACKER = umfMemoryTrackerCreate();
22+
}
1923

2024
static void umfDestroy(void) {
2125
umfMemoryTrackerDestroy(TRACKER);

src/utils/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (C) 2023 Intel Corporation
1+
# Copyright (C) 2023-2024 Intel Corporation
22
# Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT.
33
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
44

@@ -7,6 +7,7 @@ include(FindThreads)
77

88
set(UMF_UTILS_SOURCES_COMMON
99
utils_common.c
10+
utils_log.c
1011
)
1112

1213
set(UMF_UTILS_SOURCES_POSIX

src/utils/utils_common.c

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
*
88
*/
99

10-
#include "utils_common.h"
10+
#include <string.h>
11+
1112
#include "utils_assert.h"
13+
#include "utils_common.h"
1214

1315
// align a pointer and a size
1416
void util_align_ptr_size(void **ptr, size_t *size, size_t alignment) {
@@ -33,3 +35,34 @@ void util_align_ptr_size(void **ptr, size_t *size, size_t alignment) {
3335
int util_is_running_in_proxy_lib(void) {
3436
return util_env_var_has_str("LD_PRELOAD", "libumf_proxy.so");
3537
}
38+
39+
const char *util_parse_var(const char *var, const char *option,
40+
const char **extraArg) {
41+
const char *found = strstr(var, option);
42+
// ensure that found string is first on list or it's a separating semicolon
43+
if (!found) {
44+
return NULL;
45+
}
46+
// if found string is not at the beginning of var ensure it's preceded by ';'
47+
if (found != var && *(found - 1) != ';') {
48+
return NULL;
49+
}
50+
51+
const char *endFound = found + strlen(option);
52+
if (!extraArg) {
53+
// if there is no argument, matched string should end with ';' or '\0'
54+
if (*endFound != '\0' && *endFound != ';') {
55+
return NULL;
56+
}
57+
return found;
58+
}
59+
60+
// check if matched string ends with ','
61+
if (*endFound != ',') {
62+
return NULL;
63+
}
64+
65+
*extraArg = endFound + 1;
66+
67+
return found;
68+
}

src/utils/utils_common.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ extern "C" {
4343

4444
#endif /* _WIN32 */
4545

46-
// util_env_var - populate the given buffer with the value
46+
// utils_env_var - populate the given buffer with the value
4747
// of the given environment variable
4848
// Return value
4949
// If the function succeeds, the return value is the number of characters
@@ -61,6 +61,23 @@ int util_env_var(const char *envvar, char *buffer, size_t buffer_size);
6161
// Check if the environment variable contains the given string.
6262
int util_env_var_has_str(const char *envvar, const char *str);
6363

64+
// util_parse_var - Parses var for a prefix,
65+
// optionally identifying a following argument.
66+
// Parameters:
67+
// - var: String to parse in "option1;option2,arg2;..." format, with options
68+
// separated by ';' and optional arguments by ','.
69+
// - option: Option to search for within var.
70+
// - extraArg: If not NULL, function expects an argument after the option and
71+
// updates this pointer to the argument's position within var.
72+
// If NULL, function expects option without an argument.
73+
// Return Value:
74+
// Pointer to option within var if found; NULL otherwise.
75+
//
76+
// IMPORTANT: Both extraArg and return values are pointers within var,
77+
// and are not null-terminated.
78+
const char *util_parse_var(const char *var, const char *option,
79+
const char **extraArg);
80+
6481
// check if we are running in the proxy library
6582
int util_is_running_in_proxy_lib(void);
6683

src/utils/utils_log.c

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
/*
2+
*
3+
* Copyright (C) 2024 Intel Corporation
4+
*
5+
* Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT.
6+
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
*
8+
*/
9+
10+
#ifdef _WIN32
11+
#include <windows.h>
12+
#else
13+
#define _GNU_SOURCE 1
14+
#include <sys/syscall.h>
15+
#include <sys/types.h>
16+
#include <unistd.h>
17+
#endif
18+
19+
#ifdef __APPLE__
20+
#include <pthread.h>
21+
#endif
22+
23+
#include <stdarg.h>
24+
#include <stdio.h>
25+
#include <string.h>
26+
#include <time.h>
27+
28+
#include "utils_assert.h"
29+
#include "utils_common.h"
30+
#include "utils_log.h"
31+
32+
#define LOG_MAX 8192
33+
#define LOG_HEADER 256
34+
#define MAX_FILE_PATH 256
35+
#define MAX_ENV_LEN 2048
36+
37+
typedef struct {
38+
int timestamp;
39+
int pid;
40+
util_log_level_t level;
41+
util_log_level_t flushLevel;
42+
FILE *output;
43+
} util_log_config_t;
44+
45+
util_log_config_t loggerConfig = {0, 0, LOG_ERROR, LOG_ERROR, NULL};
46+
47+
static const char *level_to_str(util_log_level_t l) {
48+
switch (l) {
49+
case LOG_DEBUG:
50+
return "DEBUG";
51+
case LOG_ERROR:
52+
return "ERROR";
53+
case LOG_INFO:
54+
return "INFO";
55+
case LOG_WARNING:
56+
return "WARN";
57+
default:
58+
ASSERT(0);
59+
return "";
60+
}
61+
}
62+
63+
void util_log(util_log_level_t level, const char *format, ...) {
64+
if (!loggerConfig.output) {
65+
return; //logger not enabled
66+
}
67+
if (level < loggerConfig.level) {
68+
return;
69+
}
70+
71+
#if defined(_WIN32)
72+
DWORD pid = GetCurrentProcessId();
73+
DWORD tid = GetCurrentThreadId();
74+
#elif defined(__APPLE__)
75+
pid_t pid = getpid();
76+
uint64_t tid64;
77+
pthread_threadid_np(NULL, &tid64);
78+
pid_t tid = (pid_t)tid64;
79+
#else
80+
pid_t pid = getpid();
81+
pid_t tid = gettid();
82+
#endif
83+
84+
char buffer[LOG_MAX];
85+
va_list args;
86+
va_start(args, format);
87+
int ret = vsnprintf(buffer, sizeof(buffer), format, args);
88+
const char *overflow = "";
89+
if (ret >= (intptr_t)sizeof(buffer)) {
90+
//TODO: alloc bigger buffer with base alloc
91+
overflow = "[truncated...]";
92+
}
93+
va_end(args);
94+
char header[LOG_HEADER];
95+
char *h_pos = header;
96+
memset(header, 0, sizeof(header));
97+
98+
if (loggerConfig.timestamp) {
99+
time_t now = time(NULL);
100+
struct tm tm_info;
101+
#ifdef _WIN32
102+
localtime_s(&tm_info, &now);
103+
#else
104+
localtime_r(&now, &tm_info);
105+
#endif
106+
107+
ASSERT((intptr_t)sizeof(header) > (h_pos - header));
108+
h_pos += strftime(h_pos, sizeof(header) - (h_pos - header),
109+
"%Y-%m-%dT%H:%M:%S ", &tm_info);
110+
}
111+
112+
if (loggerConfig.pid) {
113+
ASSERT((intptr_t)sizeof(header) > (h_pos - header));
114+
h_pos += snprintf(h_pos, sizeof(header) - (h_pos - header),
115+
"PID:%-6lu TID:%-6lu ", (unsigned long)pid,
116+
(unsigned long)tid);
117+
}
118+
119+
// We take twice header size here to ensure that
120+
// we have space for log level and overflow string
121+
// otherwise -Wformat-truncation might be thrown by compiler
122+
char logLine[LOG_MAX + LOG_HEADER * 2];
123+
snprintf(logLine, sizeof(logLine), "[%s%-5s UMF] %s%s\n", header,
124+
level_to_str(level), buffer, overflow);
125+
126+
fputs(logLine, loggerConfig.output);
127+
128+
if (level >= loggerConfig.flushLevel) {
129+
fflush(loggerConfig.output);
130+
}
131+
}
132+
133+
static const char *bool_to_str(int b) { return b ? "yes" : "no"; }
134+
135+
void util_log_init(void) {
136+
char envVar[MAX_ENV_LEN];
137+
138+
if (util_env_var("UMF_LOG", envVar, sizeof(envVar)) <= 0) {
139+
return;
140+
}
141+
142+
const char *arg;
143+
if (util_parse_var(envVar, "output:stdout", NULL)) {
144+
loggerConfig.output = stdout;
145+
} else if (util_parse_var(envVar, "output:stderr", NULL)) {
146+
loggerConfig.output = stderr;
147+
} else if (util_parse_var(envVar, "output:file", &arg)) {
148+
loggerConfig.output = NULL;
149+
const char *argEnd = strstr(arg, ";");
150+
char file[MAX_FILE_PATH + 1];
151+
size_t len = 0;
152+
153+
if (argEnd) {
154+
len = argEnd - arg;
155+
} else {
156+
len = strlen(arg);
157+
}
158+
159+
if (len <= MAX_FILE_PATH) {
160+
memcpy(file, arg, len);
161+
file[len] = '\0';
162+
#ifdef _WIN32
163+
if (fopen_s(&loggerConfig.output, file, "w")) {
164+
loggerConfig.output = NULL;
165+
}
166+
#else
167+
loggerConfig.output = fopen(file, "w");
168+
#endif
169+
}
170+
if (!loggerConfig.output) {
171+
loggerConfig.output = stderr;
172+
LOG_ERR("Cannot open output file %s - logging disabled", file);
173+
loggerConfig.output = NULL;
174+
return;
175+
}
176+
}
177+
178+
else {
179+
loggerConfig.output = stderr;
180+
LOG_ERR("Logging output not set - logging disabled");
181+
loggerConfig.output = NULL;
182+
return;
183+
}
184+
185+
if (util_parse_var(envVar, "timestamp:yes", NULL)) {
186+
loggerConfig.timestamp = 1;
187+
} else if (util_parse_var(envVar, "timestamp:no", NULL)) {
188+
loggerConfig.timestamp = 0;
189+
}
190+
191+
if (util_parse_var(envVar, "pid:yes", NULL)) {
192+
loggerConfig.pid = 1;
193+
} else if (util_parse_var(envVar, "pid:no", NULL)) {
194+
loggerConfig.pid = 0;
195+
}
196+
197+
if (util_parse_var(envVar, "level:debug", NULL)) {
198+
loggerConfig.level = LOG_DEBUG;
199+
} else if (util_parse_var(envVar, "level:info", NULL)) {
200+
loggerConfig.level = LOG_INFO;
201+
} else if (util_parse_var(envVar, "level:warning", NULL)) {
202+
loggerConfig.level = LOG_WARNING;
203+
} else if (util_parse_var(envVar, "level:error", NULL)) {
204+
loggerConfig.level = LOG_ERROR;
205+
}
206+
207+
if (util_parse_var(envVar, "flush:debug", NULL)) {
208+
loggerConfig.flushLevel = LOG_DEBUG;
209+
} else if (util_parse_var(envVar, "flush:info", NULL)) {
210+
loggerConfig.flushLevel = LOG_INFO;
211+
} else if (util_parse_var(envVar, "flush:warning", NULL)) {
212+
loggerConfig.flushLevel = LOG_WARNING;
213+
} else if (util_parse_var(envVar, "flush:error", NULL)) {
214+
loggerConfig.flushLevel = LOG_ERROR;
215+
}
216+
217+
LOG_INFO(
218+
"Logger enabled (level: %s, flush: %s, pid: %s, timestamp: %s)",
219+
level_to_str(loggerConfig.level), level_to_str(loggerConfig.flushLevel),
220+
bool_to_str(loggerConfig.pid), bool_to_str(loggerConfig.timestamp));
221+
}

0 commit comments

Comments
 (0)