/src/libwebsockets/lib/core/logs.c
Line | Count | Source |
1 | | /* |
2 | | * libwebsockets - small server side websockets and web server implementation |
3 | | * |
4 | | * Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com> |
5 | | * |
6 | | * Permission is hereby granted, free of charge, to any person obtaining a copy |
7 | | * of this software and associated documentation files (the "Software"), to |
8 | | * deal in the Software without restriction, including without limitation the |
9 | | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
10 | | * sell copies of the Software, and to permit persons to whom the Software is |
11 | | * furnished to do so, subject to the following conditions: |
12 | | * |
13 | | * The above copyright notice and this permission notice shall be included in |
14 | | * all copies or substantial portions of the Software. |
15 | | * |
16 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
19 | | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
20 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
21 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
22 | | * IN THE SOFTWARE. |
23 | | */ |
24 | | |
25 | | #include "private-lib-core.h" |
26 | | |
27 | | #ifdef LWS_HAVE_SYS_TYPES_H |
28 | | #include <sys/types.h> |
29 | | #endif |
30 | | |
31 | | #if defined(LWS_PLAT_OPTEE) |
32 | | void lwsl_emit_optee(int level, const char *line); |
33 | | #endif |
34 | | |
35 | | lws_log_cx_t log_cx = { |
36 | | #if !defined(LWS_PLAT_OPTEE) |
37 | | .u.emit = lwsl_emit_stderr, |
38 | | #else |
39 | | .u.emit = lwsl_emit_optee, |
40 | | #endif |
41 | | .lll_flags = LLL_ERR | LLL_WARN | LLL_NOTICE, |
42 | | }; |
43 | | |
44 | | #if !defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_NO_LOGS) |
45 | | static const char * log_level_names ="EWNIDPHXCLUT??"; |
46 | | #endif |
47 | | |
48 | | /* |
49 | | * Name an instance tag and attach to a group |
50 | | */ |
51 | | |
52 | | void |
53 | | __lws_lc_tag(struct lws_context *context, lws_lifecycle_group_t *grp, |
54 | | lws_lifecycle_t *lc, const char *format, ...) |
55 | 0 | { |
56 | 0 | va_list ap; |
57 | 0 | int n = 1; |
58 | |
|
59 | 0 | if (*lc->gutag == '[') { |
60 | | /* appending inside [] */ |
61 | |
|
62 | 0 | char *cp = (char *)strchr(lc->gutag, ']'); |
63 | 0 | char rend[96]; |
64 | 0 | size_t ll, k; |
65 | 0 | int n; |
66 | |
|
67 | 0 | if (!cp) |
68 | 0 | return; |
69 | | |
70 | | /* length of closing brace and anything else after it */ |
71 | 0 | k = strlen(cp); |
72 | | |
73 | | /* compute the remaining gutag unused */ |
74 | 0 | ll = sizeof(lc->gutag) - lws_ptr_diff_size_t(cp, lc->gutag) - k - 1; |
75 | 0 | if (ll > sizeof(rend) - 1) |
76 | 0 | ll = sizeof(rend) - 1; |
77 | 0 | va_start(ap, format); |
78 | 0 | n = vsnprintf(rend, ll, format, ap); |
79 | 0 | va_end(ap); |
80 | |
|
81 | 0 | if ((unsigned int)n > ll) |
82 | 0 | n = (int)ll; |
83 | | |
84 | | /* shove the trailer up by what we added */ |
85 | 0 | memmove(cp + n, cp, k); |
86 | 0 | assert(k + (unsigned int)n < sizeof(lc->gutag)); |
87 | 0 | cp[k + (unsigned int)n] = '\0'; |
88 | | /* copy what we added into place */ |
89 | 0 | memcpy(cp, rend, (unsigned int)n); |
90 | |
|
91 | 0 | return; |
92 | 0 | } |
93 | | |
94 | 0 | assert(grp); |
95 | 0 | assert(grp->tag_prefix); /* lc group must have a tag prefix string */ |
96 | |
|
97 | 0 | lc->gutag[0] = '['; |
98 | |
|
99 | | #if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) /* ie, will have getpid if set */ |
100 | | n += lws_snprintf(&lc->gutag[n], sizeof(lc->gutag) - |
101 | | (unsigned int)n - 1u, "%u|", getpid()); |
102 | | #endif |
103 | 0 | n += lws_snprintf(&lc->gutag[n], sizeof(lc->gutag) - |
104 | 0 | (unsigned int)n - 1u, "%s|%lx|", |
105 | 0 | grp->tag_prefix, |
106 | 0 | (unsigned long)grp->ordinal++); |
107 | |
|
108 | 0 | va_start(ap, format); |
109 | 0 | n += vsnprintf(&lc->gutag[n], sizeof(lc->gutag) - (unsigned int)n - |
110 | 0 | 1u, format, ap); |
111 | 0 | va_end(ap); |
112 | |
|
113 | 0 | if (n < (int)sizeof(lc->gutag) - 2) { |
114 | 0 | lc->gutag[n++] = ']'; |
115 | 0 | lc->gutag[n++] = '\0'; |
116 | 0 | } else { |
117 | 0 | lc->gutag[sizeof(lc->gutag) - 2] = ']'; |
118 | 0 | lc->gutag[sizeof(lc->gutag) - 1] = '\0'; |
119 | 0 | } |
120 | |
|
121 | 0 | lc->us_creation = (uint64_t)lws_now_usecs(); |
122 | 0 | lws_dll2_add_tail(&lc->list, &grp->owner); |
123 | |
|
124 | 0 | lwsl_refcount_cx(lc->log_cx, 1); |
125 | |
|
126 | 0 | #if defined(LWS_LOG_TAG_LIFECYCLE) |
127 | 0 | lwsl_cx_info(context, " ++ %s (%d)", lc->gutag, (int)lws_dll2_count(&grp->owner)); |
128 | 0 | #endif |
129 | 0 | } |
130 | | |
131 | | /* |
132 | | * Normally we want to set the tag one time at creation. But sometimes we |
133 | | * don't have enough information at that point to give it a meaningful tag, eg, |
134 | | * it's an accepted, served connection but we haven't read data from it yet |
135 | | * to find out what it wants to be. |
136 | | * |
137 | | * This allows you to append some extra info to the tag in those cases, the |
138 | | * initial tag remains the same on the lhs so it can be tracked correctly. |
139 | | */ |
140 | | |
141 | | void |
142 | | __lws_lc_tag_append(lws_lifecycle_t *lc, const char *app) |
143 | 0 | { |
144 | 0 | int n = (int)strlen(lc->gutag); |
145 | |
|
146 | 0 | if (n && lc->gutag[n - 1] == ']') |
147 | 0 | n--; |
148 | |
|
149 | 0 | if (!lc->recycle_len) |
150 | 0 | lc->recycle_len = (uint8_t)n; |
151 | 0 | else |
152 | 0 | n = lc->recycle_len; |
153 | |
|
154 | 0 | if ((unsigned int)n + 2u >= sizeof(lc->gutag)) { |
155 | | /* |
156 | | * No room to append anything... sizeof(gutag) - 2 - n would |
157 | | * underflow to a huge size_t and lws_snprintf() only rejects |
158 | | * size 0. Just make sure the tag is closed and terminated. |
159 | | */ |
160 | 0 | lc->gutag[sizeof(lc->gutag) - 2] = ']'; |
161 | 0 | lc->gutag[sizeof(lc->gutag) - 1] = '\0'; |
162 | |
|
163 | 0 | return; |
164 | 0 | } |
165 | | |
166 | 0 | n += lws_snprintf(&lc->gutag[n], sizeof(lc->gutag) - 2u - |
167 | 0 | (unsigned int)n, "|%s]", app); |
168 | |
|
169 | 0 | if ((unsigned int)n >= sizeof(lc->gutag) - 2u) { |
170 | 0 | lc->gutag[sizeof(lc->gutag) - 2] = ']'; |
171 | 0 | lc->gutag[sizeof(lc->gutag) - 1] = '\0'; |
172 | 0 | } |
173 | 0 | } |
174 | | |
175 | | /* |
176 | | * Remove instance from group |
177 | | */ |
178 | | |
179 | | void |
180 | | __lws_lc_untag(struct lws_context *context, lws_lifecycle_t *lc) |
181 | 0 | { |
182 | | //lws_lifecycle_group_t *grp; |
183 | 0 | char buf[24]; |
184 | |
|
185 | 0 | if (!lc->gutag[0]) { /* we never tagged this object... */ |
186 | 0 | lwsl_cx_err(context, "%s never tagged", lc->gutag); |
187 | 0 | assert(0); |
188 | 0 | return; |
189 | 0 | } |
190 | | |
191 | 0 | if (!lws_dll2_owner(&lc->list)) { /* we already untagged this object... */ |
192 | 0 | lwsl_cx_err(context, "%s untagged twice", lc->gutag); |
193 | 0 | assert(0); |
194 | 0 | return; |
195 | 0 | } |
196 | | |
197 | | //grp = lws_container_of(lc->list.owner, lws_lifecycle_group_t, owner); |
198 | | |
199 | 0 | #if defined(LWS_LOG_TAG_LIFECYCLE) |
200 | 0 | if (lws_humanize(buf, sizeof(buf), |
201 | 0 | (uint64_t)lws_now_usecs() - lc->us_creation, |
202 | 0 | humanize_schema_us) > 0) |
203 | | |
204 | 0 | lwsl_cx_info(context, " -- %s (%d) %s", lc->gutag, |
205 | 0 | (int)lws_dll2_count(lws_dll2_owner(&lc->list)) - 1, buf); |
206 | 0 | #endif |
207 | |
|
208 | 0 | lws_dll2_remove(&lc->list); |
209 | |
|
210 | 0 | lwsl_refcount_cx(lc->log_cx, -1); |
211 | 0 | } |
212 | | |
213 | | const char * |
214 | | lws_lc_tag(lws_lifecycle_t *lc) |
215 | 0 | { |
216 | 0 | return lc->gutag; |
217 | 0 | } |
218 | | |
219 | | |
220 | | int |
221 | | lwsl_timestamp(int level, char *p, size_t len) |
222 | 187 | { |
223 | 187 | #if !defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_NO_LOGS) |
224 | 187 | time_t o_now; |
225 | 187 | unsigned long long now; |
226 | 187 | struct timeval tv; |
227 | 187 | struct tm *ptm = NULL; |
228 | 187 | #if defined(LWS_HAVE_LOCALTIME_R) |
229 | 187 | struct tm tm; |
230 | 187 | #endif |
231 | 187 | int n; |
232 | | |
233 | 187 | gettimeofday(&tv, NULL); |
234 | 187 | o_now = tv.tv_sec; |
235 | 187 | now = ((unsigned long long)tv.tv_sec * 10000) + |
236 | 187 | (unsigned int)(tv.tv_usec / 100); |
237 | | |
238 | 187 | #if defined(LWS_HAVE_LOCALTIME_R) |
239 | 187 | ptm = localtime_r(&o_now, &tm); |
240 | | #else |
241 | | ptm = localtime(&o_now); |
242 | | #endif |
243 | 187 | p[0] = '\0'; |
244 | 187 | for (n = 0; n < LLL_COUNT; n++) { |
245 | 187 | if (level != (1 << n)) |
246 | 0 | continue; |
247 | | |
248 | 187 | if (ptm) |
249 | 187 | n = lws_snprintf(p, len, |
250 | 187 | "[%04d/%02d/%02d %02d:%02d:%02d:%04d] %c: ", |
251 | 187 | ptm->tm_year + 1900, |
252 | 187 | ptm->tm_mon + 1, |
253 | 187 | ptm->tm_mday, |
254 | 187 | ptm->tm_hour, |
255 | 187 | ptm->tm_min, |
256 | 187 | ptm->tm_sec, |
257 | 187 | (int)(now % 10000), log_level_names[n]); |
258 | 0 | else |
259 | 0 | n = lws_snprintf(p, len, "[%llu:%04d] %c: ", |
260 | 0 | (unsigned long long) now / 10000, |
261 | 0 | (int)(now % 10000), log_level_names[n]); |
262 | | |
263 | | #if defined(LWS_PLAT_FREERTOS) |
264 | | n += lws_snprintf(p + n, len - n, "%6u: ", |
265 | | #if defined(LWS_AMAZON_RTOS) |
266 | | (unsigned int)xPortGetFreeHeapSize()); |
267 | | #else |
268 | | (unsigned int)esp_get_free_heap_size()); |
269 | | #endif |
270 | | #endif |
271 | | |
272 | 187 | return n; |
273 | 187 | } |
274 | | #else |
275 | | p[0] = '\0'; |
276 | | #endif |
277 | | |
278 | 0 | return 0; |
279 | 187 | } |
280 | | |
281 | | uint32_t |
282 | | lws_log_ratelimit_check(lws_log_ratelimit_t *rl, int64_t interval_us) |
283 | 0 | { |
284 | 0 | lws_usec_t now = lws_now_usecs(); |
285 | |
|
286 | 0 | if (now >= rl->next_log_us) { |
287 | 0 | uint32_t r = rl->dropped + 1; |
288 | 0 | rl->next_log_us = now + interval_us; |
289 | 0 | rl->dropped = 0; |
290 | 0 | return r; |
291 | 0 | } |
292 | | |
293 | 0 | rl->dropped++; |
294 | |
|
295 | 0 | return 0; |
296 | 0 | } |
297 | | |
298 | | |
299 | | #ifndef LWS_PLAT_OPTEE |
300 | | static const char * const colours[] = { |
301 | | "[31;1m", /* LLL_ERR */ |
302 | | "[36;1m", /* LLL_WARN */ |
303 | | "[35;1m", /* LLL_NOTICE */ |
304 | | "[32;1m", /* LLL_INFO */ |
305 | | "[34;1m", /* LLL_DEBUG */ |
306 | | "[33;1m", /* LLL_PARSER */ |
307 | | "[33m", /* LLL_HEADER */ |
308 | | "[33m", /* LLL_EXT */ |
309 | | "[33m", /* LLL_CLIENT */ |
310 | | "[33;1m", /* LLL_LATENCY */ |
311 | | "[0;1m", /* LLL_USER */ |
312 | | "[31m", /* LLL_THREAD */ |
313 | | }; |
314 | | |
315 | | static char tty; |
316 | | |
317 | | static void |
318 | | _lwsl_emit_stderr(int level, const char *line) |
319 | 148 | { |
320 | 148 | int n, m = LWS_ARRAY_SIZE(colours) - 1; |
321 | | |
322 | 148 | if (!tty) |
323 | 1 | tty = (char)(isatty(2) | 2); |
324 | | |
325 | 148 | if (tty == 3) { |
326 | 0 | n = 1 << (LWS_ARRAY_SIZE(colours) - 1); |
327 | 0 | while (n) { |
328 | 0 | if (level & n) |
329 | 0 | break; |
330 | 0 | m--; |
331 | 0 | n >>= 1; |
332 | 0 | } |
333 | 0 | fprintf(stderr, "%c%s%s%c[0m", 27, colours[m], line, 27); |
334 | 0 | } else |
335 | 148 | fprintf(stderr, "%s", line); |
336 | 148 | } |
337 | | |
338 | | void |
339 | | lwsl_emit_stderr(int level, const char *line) |
340 | 148 | { |
341 | 148 | _lwsl_emit_stderr(level, line); |
342 | 148 | } |
343 | | |
344 | | void |
345 | | lwsl_emit_stderr_notimestamp(int level, const char *line) |
346 | 0 | { |
347 | 0 | _lwsl_emit_stderr(level, line); |
348 | 0 | } |
349 | | |
350 | | #if !defined(LWS_PLAT_FREERTOS) && !defined(LWS_PLAT_OPTEE) && !defined(LWS_PLAT_BAREMETAL) |
351 | | |
352 | | /* |
353 | | * Helper to emit to a file |
354 | | */ |
355 | | |
356 | | void |
357 | | lws_log_emit_cx_file(struct lws_log_cx *cx, int level, const char *line, |
358 | | size_t len) |
359 | 0 | { |
360 | 0 | int fd = (int)(intptr_t)cx->stg; |
361 | |
|
362 | 0 | if (fd >= 0) |
363 | 0 | if (write(fd, line, (unsigned int)len) != (ssize_t)len) |
364 | 0 | fprintf(stderr, "Unable to write log to file\n"); |
365 | 0 | } |
366 | | |
367 | | /* |
368 | | * Helper to use a .refcount_cb to store logs in a file |
369 | | */ |
370 | | |
371 | | void |
372 | | lws_log_use_cx_file(struct lws_log_cx *cx, int _new) |
373 | 0 | { |
374 | 0 | int fd; |
375 | |
|
376 | 0 | if (_new > 0 && cx->refcount == 1) { |
377 | 0 | fd = open((const char *)cx->opaque, |
378 | 0 | LWS_O_CREAT | LWS_O_TRUNC | LWS_O_WRONLY, 0600); |
379 | 0 | if (fd < 0) |
380 | 0 | fprintf(stderr, "Unable to open log %s: errno %d\n", |
381 | 0 | (const char *)cx->opaque, errno); |
382 | 0 | cx->stg = (void *)(intptr_t)fd; |
383 | |
|
384 | 0 | return; |
385 | 0 | } |
386 | | |
387 | 0 | fd = (int)(intptr_t)cx->stg; |
388 | |
|
389 | 0 | if (_new <= 0 && cx->refcount == 0 && fd >= 0) { |
390 | 0 | close(fd); |
391 | 0 | cx->stg = (void *)(intptr_t)-1; |
392 | 0 | } |
393 | 0 | } |
394 | | |
395 | | #endif |
396 | | |
397 | | #endif |
398 | | |
399 | | #if !(defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_NETWORK)) |
400 | | void |
401 | | __lws_logv(lws_log_cx_t *cx, lws_log_prepend_cx_t prep, void *obj, |
402 | | int filter, uint32_t dropped, const char *_fun, const char *format, va_list vl) |
403 | 12.2k | { |
404 | 12.2k | #if LWS_MAX_SMP == 1 && !defined(LWS_WITH_THREADPOOL) |
405 | | /* this is incompatible with multithreaded logging */ |
406 | 12.2k | static char buf[256], prev_buf[256]; |
407 | | #else |
408 | | char buf[1024]; |
409 | | static char prev_buf[1024]; |
410 | | #endif |
411 | 12.2k | static uint32_t log_dupes; |
412 | 12.2k | static lws_usec_t last_log_dupe_emit; |
413 | 12.2k | char *p = buf, *end = p + sizeof(buf) - 1, *body_start; |
414 | 12.2k | lws_log_cx_t *cxp; |
415 | 12.2k | int n, back = 0; |
416 | | |
417 | | /* |
418 | | * We need to handle NULL wsi etc at the wrappers as gracefully as |
419 | | * possible |
420 | | */ |
421 | | |
422 | 12.2k | if (!cx) { |
423 | 0 | lws_strncpy(p, "NULL log cx: ", sizeof(buf) - 1); |
424 | 0 | p += 13; |
425 | | /* use the processwide one for lack of anything better */ |
426 | 0 | cx = &log_cx; |
427 | 0 | } |
428 | | |
429 | 12.2k | cxp = cx; |
430 | | |
431 | 12.2k | if (!(cx->lll_flags & (uint32_t)filter)) |
432 | | /* |
433 | | * logs may be produced and built in to the code but disabled |
434 | | * at runtime |
435 | | */ |
436 | 12.0k | return; |
437 | | |
438 | | #if !defined(LWS_LOGS_TIMESTAMP) |
439 | | if (cx->lll_flags & LLLF_LOG_TIMESTAMP) |
440 | | #endif |
441 | 187 | { |
442 | 187 | buf[0] = '\0'; |
443 | 187 | lwsl_timestamp(filter, buf, sizeof(buf)); |
444 | 187 | p += strlen(buf); |
445 | 187 | } |
446 | | |
447 | 187 | body_start = p; |
448 | | |
449 | | /* |
450 | | * prepend parent log ctx content first |
451 | | * top level cx also gets an opportunity to prepend |
452 | | */ |
453 | | |
454 | 187 | while (cxp->parent) { |
455 | 0 | cxp = cxp->parent; |
456 | 0 | back++; |
457 | 0 | } |
458 | | |
459 | 187 | do { |
460 | 187 | int b = back; |
461 | | |
462 | 187 | cxp = cx; |
463 | 187 | while (b--) |
464 | 0 | cxp = cxp->parent; |
465 | 187 | if (cxp->prepend) |
466 | 0 | cxp->prepend(cxp, NULL, &p, end); |
467 | | |
468 | 187 | back--; |
469 | 187 | } while (back > 0); |
470 | | |
471 | 187 | if (prep) |
472 | 0 | prep(cxp, obj, &p, end); |
473 | | |
474 | 187 | if (_fun) |
475 | 0 | p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "%s: ", _fun); |
476 | | |
477 | | /* |
478 | | * The actual log content |
479 | | */ |
480 | | |
481 | 187 | n = vsnprintf(p, lws_ptr_diff_size_t(end, p), format, vl); |
482 | | |
483 | | /* vnsprintf returns what it would have written, even if truncated */ |
484 | 187 | if (p + n > end - 2) { |
485 | 0 | p = end - 5; |
486 | 0 | *p++ = '.'; |
487 | 0 | *p++ = '.'; |
488 | 0 | *p++ = '.'; |
489 | 0 | *p++ = '\n'; |
490 | 0 | *p++ = '\0'; |
491 | 187 | } else { |
492 | 187 | if (n > 0) { |
493 | 187 | p += n; |
494 | 187 | if (p[-1] == '\n') |
495 | 187 | p--; |
496 | 187 | if (dropped > 1) |
497 | 0 | p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), " (dropped %u logs)", (unsigned int)(dropped - 1)); |
498 | 187 | if (p < end - 1) { |
499 | 187 | *p++ = '\n'; |
500 | 187 | *p = '\0'; |
501 | 187 | } |
502 | 187 | } |
503 | 187 | } |
504 | | |
505 | 187 | if (!strcmp(body_start, prev_buf)) { |
506 | 39 | log_dupes++; |
507 | 39 | if (lws_now_usecs() - last_log_dupe_emit < 1000000) |
508 | 39 | return; |
509 | | |
510 | 0 | p = body_start + strlen(body_start); |
511 | 0 | if (p > buf && p[-1] == '\n') |
512 | 0 | p--; |
513 | 0 | p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), |
514 | 0 | " (swallowed %u dupes)\n", (unsigned int)log_dupes); |
515 | 0 | log_dupes = 0; |
516 | 0 | last_log_dupe_emit = lws_now_usecs(); |
517 | 148 | } else { |
518 | 148 | lws_strncpy(prev_buf, body_start, sizeof(prev_buf)); |
519 | 148 | log_dupes = 0; |
520 | 148 | last_log_dupe_emit = lws_now_usecs(); |
521 | 148 | } |
522 | | |
523 | | /* |
524 | | * The actual emit |
525 | | */ |
526 | | |
527 | 148 | if (cx->lll_flags & LLLF_LOG_CONTEXT_AWARE) |
528 | 0 | cx->u.emit_cx(cx, filter, buf, lws_ptr_diff_size_t(p, buf)); |
529 | 148 | else |
530 | 148 | cx->u.emit(filter, buf); |
531 | 148 | } |
532 | | |
533 | | void _lws_logv(int filter, const char *format, va_list vl) |
534 | 0 | { |
535 | 0 | __lws_logv(&log_cx, NULL, NULL, filter, 0, NULL, format, vl); |
536 | 0 | } |
537 | | |
538 | | void _lws_log(int filter, const char *format, ...) |
539 | 12.2k | { |
540 | 12.2k | va_list ap; |
541 | | |
542 | 12.2k | va_start(ap, format); |
543 | 12.2k | __lws_logv(&log_cx, NULL, NULL, filter, 0, NULL, format, ap); |
544 | 12.2k | va_end(ap); |
545 | 12.2k | } |
546 | | |
547 | | void _lws_log_rl(int filter, uint32_t dropped, const char *format, ...) |
548 | 0 | { |
549 | 0 | va_list ap; |
550 | |
|
551 | 0 | va_start(ap, format); |
552 | 0 | __lws_logv(&log_cx, NULL, NULL, filter, dropped, NULL, format, ap); |
553 | 0 | va_end(ap); |
554 | 0 | } |
555 | | |
556 | | void _lws_log_cx(lws_log_cx_t *cx, lws_log_prepend_cx_t prep, void *obj, |
557 | | int filter, const char *_fun, const char *format, ...) |
558 | 0 | { |
559 | 0 | va_list ap; |
560 | |
|
561 | 0 | if (!cx) |
562 | 0 | cx = &log_cx; |
563 | |
|
564 | 0 | va_start(ap, format); |
565 | 0 | __lws_logv(cx, prep, obj, filter, 0, _fun, format, ap); |
566 | 0 | va_end(ap); |
567 | 0 | } |
568 | | |
569 | | void _lws_log_cx_rl(lws_log_cx_t *cx, lws_log_prepend_cx_t prep, void *obj, |
570 | | int filter, uint32_t dropped, const char *_fun, const char *format, ...) |
571 | 0 | { |
572 | 0 | va_list ap; |
573 | |
|
574 | 0 | if (!cx) |
575 | 0 | cx = &log_cx; |
576 | |
|
577 | 0 | va_start(ap, format); |
578 | 0 | __lws_logv(cx, prep, obj, filter, dropped, _fun, format, ap); |
579 | 0 | va_end(ap); |
580 | 0 | } |
581 | | #endif |
582 | | |
583 | | void |
584 | | lws_set_log_level(int flags, lws_log_emit_t func) |
585 | 0 | { |
586 | 0 | log_cx.lll_flags = (uint32_t)(flags & (~LLLF_LOG_CONTEXT_AWARE)); |
587 | |
|
588 | 0 | if (func) |
589 | 0 | log_cx.u.emit = func; |
590 | 0 | } |
591 | | |
592 | | int lwsl_visible(int level) |
593 | 0 | { |
594 | 0 | return !!(log_cx.lll_flags & (uint32_t)level); |
595 | 0 | } |
596 | | |
597 | | int lwsl_visible_cx(lws_log_cx_t *cx, int level) |
598 | 0 | { |
599 | 0 | return !!(cx->lll_flags & (uint32_t)level); |
600 | 0 | } |
601 | | |
602 | | void |
603 | | lwsl_refcount_cx(lws_log_cx_t *cx, int _new) |
604 | 0 | { |
605 | | #if LWS_MAX_SMP > 1 |
606 | | volatile lws_log_cx_t *vcx = (volatile lws_log_cx_t *)cx; |
607 | | #endif |
608 | |
|
609 | 0 | if (!cx) |
610 | 0 | return; |
611 | | |
612 | | #if LWS_MAX_SMP > 1 |
613 | | if (!vcx->inited) { |
614 | | vcx->inited = 1; |
615 | | lws_pthread_mutex_init(&cx->refcount_lock); |
616 | | vcx->inited = 2; |
617 | | } |
618 | | while (vcx->inited != 2) |
619 | | ; |
620 | | lws_pthread_mutex_lock(&cx->refcount_lock); |
621 | | #endif |
622 | | |
623 | 0 | if (_new > 0) |
624 | 0 | cx->refcount++; |
625 | 0 | else { |
626 | 0 | assert(cx->refcount); |
627 | 0 | cx->refcount--; |
628 | 0 | } |
629 | |
|
630 | 0 | if (cx->refcount_cb) |
631 | 0 | cx->refcount_cb(cx, _new); |
632 | |
|
633 | | #if LWS_MAX_SMP > 1 |
634 | | lws_pthread_mutex_unlock(&cx->refcount_lock); |
635 | | #endif |
636 | 0 | } |
637 | | |
638 | | void |
639 | | lwsl_hexdump_level_cx(lws_log_cx_t *cx, lws_log_prepend_cx_t prep, void *obj, |
640 | | int hexdump_level, const void *vbuf, size_t len) |
641 | 0 | { |
642 | 0 | unsigned char *buf = (unsigned char *)vbuf; |
643 | 0 | unsigned int n; |
644 | |
|
645 | 0 | if (!lwsl_visible_cx(cx, hexdump_level)) |
646 | 0 | return; |
647 | | |
648 | 0 | if (!len) { |
649 | 0 | _lws_log_cx(cx, prep, obj, hexdump_level, NULL, |
650 | 0 | "(hexdump: zero length)\n"); |
651 | 0 | return; |
652 | 0 | } |
653 | | |
654 | 0 | if (!vbuf) { |
655 | 0 | _lws_log_cx(cx, prep, obj, hexdump_level, NULL, |
656 | 0 | "(hexdump: NULL ptr)\n"); |
657 | 0 | return; |
658 | 0 | } |
659 | | |
660 | 0 | _lws_log_cx(cx, prep, obj, hexdump_level, NULL, "\n"); |
661 | |
|
662 | 0 | for (n = 0; n < len;) { |
663 | 0 | unsigned int start = n, m; |
664 | 0 | char line[80], *p = line; |
665 | |
|
666 | 0 | p += lws_snprintf(p, 10, "%04X: ", start); |
667 | |
|
668 | 0 | for (m = 0; m < 16 && n < len; m++) |
669 | 0 | p += lws_snprintf(p, 5, "%02X ", buf[n++]); |
670 | 0 | while (m++ < 16) |
671 | 0 | p += lws_snprintf(p, 5, " "); |
672 | |
|
673 | 0 | p += lws_snprintf(p, 6, " "); |
674 | |
|
675 | 0 | for (m = 0; m < 16 && (start + m) < len; m++) { |
676 | 0 | if (buf[start + m] >= ' ' && buf[start + m] < 127) |
677 | 0 | *p++ = (char)buf[start + m]; |
678 | 0 | else |
679 | 0 | *p++ = '.'; |
680 | 0 | } |
681 | 0 | while (m++ < 16) |
682 | 0 | *p++ = ' '; |
683 | |
|
684 | 0 | *p++ = '\n'; |
685 | 0 | *p = '\0'; |
686 | 0 | _lws_log_cx(cx, prep, obj, hexdump_level, NULL, "%s", line); |
687 | 0 | (void)line; |
688 | 0 | } |
689 | |
|
690 | 0 | _lws_log_cx(cx, prep, obj, hexdump_level, NULL, "\n"); |
691 | 0 | } |
692 | | |
693 | | void |
694 | | lwsl_hexdump_level(int hexdump_level, const void *vbuf, size_t len) |
695 | 0 | { |
696 | 0 | lwsl_hexdump_level_cx(&log_cx, NULL, NULL, hexdump_level, vbuf, len); |
697 | 0 | } |
698 | | |
699 | | void |
700 | | lwsl_hexdump(const void *vbuf, size_t len) |
701 | 0 | { |
702 | 0 | #if defined(_DEBUG) |
703 | 0 | lwsl_hexdump_level(LLL_DEBUG, vbuf, len); |
704 | 0 | #endif |
705 | 0 | } |