Coverage Report

Created: 2026-10-16 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
  /*
42
   * Default for code that logs before or without any lws_context,
43
   * or that never calls lws_set_log_level(): USER is included so
44
   * a tool's own output (eg, a test's FAIL lines) is not silently
45
   * filtered
46
   */
47
  .lll_flags      = LLL_ERR | LLL_WARN | LLL_NOTICE |
48
            LLL_USER,
49
};
50
51
#if !defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_NO_LOGS)
52
static const char * log_level_names ="EWNIDPHXCLUT??";
53
#endif
54
55
/*
56
 * Name an instance tag and attach to a group
57
 */
58
59
void
60
__lws_lc_tag(struct lws_context *context, lws_lifecycle_group_t *grp,
61
       lws_lifecycle_t *lc, const char *format, ...)
62
0
{
63
0
  va_list ap;
64
0
  int n = 1;
65
66
0
  if (*lc->gutag == '[') {
67
    /* appending inside [] */
68
69
0
    char *cp = (char *)strchr(lc->gutag, ']');
70
0
    char rend[96];
71
0
    size_t ll, k;
72
0
    int n;
73
74
0
    if (!cp)
75
0
      return;
76
77
    /* length of closing brace and anything else after it */
78
0
    k = strlen(cp);
79
80
    /* compute the remaining gutag unused */
81
0
    ll = sizeof(lc->gutag) - lws_ptr_diff_size_t(cp, lc->gutag) - k - 1;
82
0
    if (ll > sizeof(rend) - 1)
83
0
      ll = sizeof(rend) - 1;
84
0
    va_start(ap, format);
85
0
    n = vsnprintf(rend, ll, format, ap);
86
0
    va_end(ap);
87
88
0
    if ((unsigned int)n > ll)
89
0
      n = (int)ll;
90
91
    /* shove the trailer up by what we added */
92
0
    memmove(cp + n, cp, k);
93
0
    assert(k + (unsigned int)n < sizeof(lc->gutag));
94
0
    cp[k + (unsigned int)n] = '\0';
95
    /* copy what we added into place */
96
0
    memcpy(cp, rend, (unsigned int)n);
97
98
0
    return;
99
0
  }
100
101
0
  assert(grp);
102
0
  assert(grp->tag_prefix); /* lc group must have a tag prefix string */
103
104
0
  lc->gutag[0] = '[';
105
106
#if defined(LWS_WITH_SECURE_STREAMS_PROXY_API) /* ie, will have getpid if set */
107
  n += lws_snprintf(&lc->gutag[n], sizeof(lc->gutag) -
108
           (unsigned int)n - 1u, "%u|", getpid());
109
#endif
110
0
  n += lws_snprintf(&lc->gutag[n], sizeof(lc->gutag) -
111
0
           (unsigned int)n - 1u, "%s|%lx|",
112
0
           grp->tag_prefix,
113
0
           (unsigned long)grp->ordinal++);
114
115
0
  va_start(ap, format);
116
0
  n += vsnprintf(&lc->gutag[n], sizeof(lc->gutag) - (unsigned int)n -
117
0
      1u, format, ap);
118
0
  va_end(ap);
119
120
0
  if (n < (int)sizeof(lc->gutag) - 2) {
121
0
    lc->gutag[n++] = ']';
122
0
    lc->gutag[n++] = '\0';
123
0
  } else {
124
0
    lc->gutag[sizeof(lc->gutag) - 2] = ']';
125
0
    lc->gutag[sizeof(lc->gutag) - 1] = '\0';
126
0
  }
127
128
0
  lc->us_creation = (uint64_t)lws_now_usecs();
129
0
  lws_dll2_add_tail(&lc->list, &grp->owner);
130
131
0
  lwsl_refcount_cx(lc->log_cx, 1);
132
133
0
#if defined(LWS_LOG_TAG_LIFECYCLE)
134
0
  lwsl_cx_info(context, " ++ %s (%d)", lc->gutag, (int)lws_dll2_count(&grp->owner));
135
0
#endif
136
0
}
137
138
/*
139
 * Normally we want to set the tag one time at creation.  But sometimes we
140
 * don't have enough information at that point to give it a meaningful tag, eg,
141
 * it's an accepted, served connection but we haven't read data from it yet
142
 * to find out what it wants to be.
143
 *
144
 * This allows you to append some extra info to the tag in those cases, the
145
 * initial tag remains the same on the lhs so it can be tracked correctly.
146
 */
147
148
void
149
__lws_lc_tag_append(lws_lifecycle_t *lc, const char *app)
150
0
{
151
0
  int n = (int)strlen(lc->gutag);
152
153
0
  if (n && lc->gutag[n - 1] == ']')
154
0
    n--;
155
156
0
  if (!lc->recycle_len)
157
0
    lc->recycle_len = (uint8_t)n;
158
0
  else
159
0
    n = lc->recycle_len;
160
161
0
  if ((unsigned int)n + 2u >= sizeof(lc->gutag)) {
162
    /*
163
     * No room to append anything... sizeof(gutag) - 2 - n would
164
     * underflow to a huge size_t and lws_snprintf() only rejects
165
     * size 0.  Just make sure the tag is closed and terminated.
166
     */
167
0
    lc->gutag[sizeof(lc->gutag) - 2] = ']';
168
0
    lc->gutag[sizeof(lc->gutag) - 1] = '\0';
169
170
0
    return;
171
0
  }
172
173
0
  n += lws_snprintf(&lc->gutag[n], sizeof(lc->gutag) - 2u -
174
0
           (unsigned int)n, "|%s]", app);
175
176
0
  if ((unsigned int)n >= sizeof(lc->gutag) - 2u) {
177
0
    lc->gutag[sizeof(lc->gutag) - 2] = ']';
178
0
    lc->gutag[sizeof(lc->gutag) - 1] = '\0';
179
0
  }
180
0
}
181
182
/*
183
 * Remove instance from group
184
 */
185
186
void
187
__lws_lc_untag(struct lws_context *context, lws_lifecycle_t *lc)
188
0
{
189
  //lws_lifecycle_group_t *grp;
190
0
  char buf[24];
191
192
0
  if (!lc->gutag[0]) { /* we never tagged this object... */
193
0
    lwsl_cx_err(context, "%s never tagged", lc->gutag);
194
0
    assert(0);
195
0
    return;
196
0
  }
197
198
0
  if (!lws_dll2_owner(&lc->list)) { /* we already untagged this object... */
199
0
    lwsl_cx_err(context, "%s untagged twice", lc->gutag);
200
0
    assert(0);
201
0
    return;
202
0
  }
203
204
  //grp = lws_container_of(lc->list.owner, lws_lifecycle_group_t, owner);
205
206
0
#if defined(LWS_LOG_TAG_LIFECYCLE)
207
0
  if (lws_humanize(buf, sizeof(buf),
208
0
         (uint64_t)lws_now_usecs() - lc->us_creation,
209
0
         humanize_schema_us) > 0)
210
211
0
  lwsl_cx_info(context, " -- %s (%d) %s", lc->gutag,
212
0
        (int)lws_dll2_count(lws_dll2_owner(&lc->list)) - 1, buf);
213
0
#endif
214
215
0
  lws_dll2_remove(&lc->list);
216
217
0
  lwsl_refcount_cx(lc->log_cx, -1);
218
0
}
219
220
const char *
221
lws_lc_tag(lws_lifecycle_t *lc)
222
0
{
223
0
  return lc->gutag;
224
0
}
225
226
227
int
228
lwsl_timestamp(int level, char *p, size_t len)
229
185
{
230
185
#if !defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_NO_LOGS)
231
185
  time_t o_now;
232
185
  unsigned long long now;
233
185
  struct timeval tv;
234
185
  struct tm *ptm = NULL;
235
185
#if defined(LWS_HAVE_LOCALTIME_R)
236
185
  struct tm tm;
237
185
#endif
238
185
  int n;
239
240
185
  gettimeofday(&tv, NULL);
241
185
  o_now = tv.tv_sec;
242
185
  now = ((unsigned long long)tv.tv_sec * 10000) +
243
185
        (unsigned int)(tv.tv_usec / 100);
244
245
185
#if defined(LWS_HAVE_LOCALTIME_R)
246
185
  ptm = localtime_r(&o_now, &tm);
247
#else
248
  ptm = localtime(&o_now);
249
#endif
250
185
  p[0] = '\0';
251
185
  for (n = 0; n < LLL_COUNT; n++) {
252
185
    if (level != (1 << n))
253
0
      continue;
254
255
185
    if (ptm)
256
185
      n = lws_snprintf(p, len,
257
185
        "[%04d/%02d/%02d %02d:%02d:%02d:%04d] %c: ",
258
185
        ptm->tm_year + 1900,
259
185
        ptm->tm_mon + 1,
260
185
        ptm->tm_mday,
261
185
        ptm->tm_hour,
262
185
        ptm->tm_min,
263
185
        ptm->tm_sec,
264
185
        (int)(now % 10000), log_level_names[n]);
265
0
    else
266
0
      n = lws_snprintf(p, len, "[%llu:%04d] %c: ",
267
0
          (unsigned long long) now / 10000,
268
0
          (int)(now % 10000), log_level_names[n]);
269
270
#if defined(LWS_PLAT_FREERTOS)
271
    n += lws_snprintf(p + n, len - n, "%6u: ",
272
#if defined(LWS_AMAZON_RTOS)
273
          (unsigned int)xPortGetFreeHeapSize());
274
#else
275
          (unsigned int)esp_get_free_heap_size());
276
#endif
277
#endif
278
279
185
    return n;
280
185
  }
281
#else
282
  p[0] = '\0';
283
#endif
284
285
0
  return 0;
286
185
}
287
288
uint32_t
289
lws_log_ratelimit_check(lws_log_ratelimit_t *rl, int64_t interval_us)
290
0
{
291
0
  lws_usec_t now = lws_now_usecs();
292
293
0
  if (now >= rl->next_log_us) {
294
0
    uint32_t r = rl->dropped + 1;
295
0
    rl->next_log_us = now + interval_us;
296
0
    rl->dropped = 0;
297
0
    return r;
298
0
  }
299
300
0
  rl->dropped++;
301
302
0
  return 0;
303
0
}
304
305
306
#ifndef LWS_PLAT_OPTEE
307
static const char * const colours[] = {
308
  "[31;1m", /* LLL_ERR */
309
  "[36;1m", /* LLL_WARN */
310
  "[35;1m", /* LLL_NOTICE */
311
  "[32;1m", /* LLL_INFO */
312
  "[34;1m", /* LLL_DEBUG */
313
  "[33;1m", /* LLL_PARSER */
314
  "[33m", /* LLL_HEADER */
315
  "[33m", /* LLL_EXT */
316
  "[33m", /* LLL_CLIENT */
317
  "[33;1m", /* LLL_LATENCY */
318
        "[0;1m", /* LLL_USER */
319
  "[31m", /* LLL_THREAD */
320
};
321
322
static char tty;
323
324
static void
325
_lwsl_emit_stderr(int level, const char *line)
326
121
{
327
121
  int n, m = LWS_ARRAY_SIZE(colours) - 1;
328
329
121
  if (!tty)
330
1
    tty = (char)(isatty(2) | 2);
331
332
121
  if (tty == 3) {
333
0
    n = 1 << (LWS_ARRAY_SIZE(colours) - 1);
334
0
    while (n) {
335
0
      if (level & n)
336
0
        break;
337
0
      m--;
338
0
      n >>= 1;
339
0
    }
340
0
    fprintf(stderr, "%c%s%s%c[0m", 27, colours[m], line, 27);
341
0
  } else
342
121
    fprintf(stderr, "%s", line);
343
121
}
344
345
void
346
lwsl_emit_stderr(int level, const char *line)
347
121
{
348
121
  _lwsl_emit_stderr(level, line);
349
121
}
350
351
void
352
lwsl_emit_stderr_notimestamp(int level, const char *line)
353
0
{
354
0
  _lwsl_emit_stderr(level, line);
355
0
}
356
357
#if !defined(LWS_PLAT_FREERTOS) && !defined(LWS_PLAT_OPTEE) && !defined(LWS_PLAT_BAREMETAL)
358
359
/*
360
 * Helper to emit to a file
361
 */
362
363
void
364
lws_log_emit_cx_file(struct lws_log_cx *cx, int level, const char *line,
365
      size_t len)
366
0
{
367
0
  int fd = (int)(intptr_t)cx->stg;
368
369
0
  if (fd >= 0)
370
0
    if (write(fd, line, (unsigned int)len) != (ssize_t)len)
371
0
      fprintf(stderr, "Unable to write log to file\n");
372
0
}
373
374
/*
375
 * Helper to use a .refcount_cb to store logs in a file
376
 */
377
378
void
379
lws_log_use_cx_file(struct lws_log_cx *cx, int _new)
380
0
{
381
0
  int fd;
382
383
0
  if (_new > 0 && cx->refcount == 1) {
384
0
    fd = open((const char *)cx->opaque,
385
0
        LWS_O_CREAT | LWS_O_TRUNC | LWS_O_WRONLY, 0600);
386
0
    if (fd < 0)
387
0
      fprintf(stderr, "Unable to open log %s: errno %d\n",
388
0
        (const char *)cx->opaque, errno);
389
0
    cx->stg = (void *)(intptr_t)fd;
390
391
0
    return;
392
0
  }
393
394
0
  fd = (int)(intptr_t)cx->stg;
395
396
0
  if (_new <= 0 && cx->refcount == 0 && fd >= 0) {
397
0
    close(fd);
398
0
    cx->stg = (void *)(intptr_t)-1;
399
0
  }
400
0
}
401
402
#endif
403
404
#endif
405
406
#if !(defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_NETWORK))
407
408
/*
409
 * More than one thread may log, not only with SMP or the threadpool: the
410
 * async queue's workers and the txpacer thread log from inside lws, and
411
 * applications log from their own threads.  So wherever there are pthreads,
412
 * except on the small embedded platforms where lws runs no threads of its
413
 * own, we must assume it.
414
 */
415
416
#if defined(LWS_HAVE_PTHREAD_H) && \
417
    (LWS_MAX_SMP > 1 || defined(LWS_WITH_THREADPOOL) || \
418
     defined(LWS_WITH_ASYNC_QUEUE) || \
419
     (!defined(LWS_PLAT_OPTEE) && !defined(LWS_PLAT_BAREMETAL) && \
420
      !defined(LWS_PLAT_FREERTOS)))
421
#define LWS_LOG_THREADED
422
#endif
423
424
#if defined(LWS_LOG_THREADED)
425
#define LWS_LOG_LINE_MAX  1024
426
#else
427
#define LWS_LOG_LINE_MAX  256
428
#endif
429
430
/*
431
 * The dupe and spew state below is processwide.  When there may be more than
432
 * one thread logging, it is guarded by a lock that is never held across a
433
 * call into the emit function, so an emit that itself logs cannot deadlock.
434
 */
435
436
#if defined(LWS_LOG_THREADED)
437
static pthread_mutex_t log_lock = PTHREAD_MUTEX_INITIALIZER;
438
182
#define log_lock_take()   pthread_mutex_lock(&log_lock)
439
182
#define log_lock_release()  pthread_mutex_unlock(&log_lock)
440
#else
441
#define log_lock_take()
442
#define log_lock_release()
443
#endif
444
445
/*
446
 * Log spew handling
447
 *
448
 * A "spew" is a sustained rate of log emission that nothing downstream can
449
 * usefully absorb: a tight loop logging every service call, a POLLOUT storm,
450
 * a state machine ping-ponging.  Left alone it fills the disk, or in a test
451
 * harness, the RAM the logs were being captured in, within seconds.
452
 *
453
 * Back-to-back identical lines are already collapsed by the dupe detection
454
 * in __lws_logv().  This catches the general case where the lines differ.
455
 *
456
 * We keep a small ring of the timestamps of the last LWS_LOG_SPEW_TS_RING
457
 * lines that got as far as emission.  When the whole ring spans less than
458
 * LWS_LOG_SPEW_ENTER_US, ie, the sustained rate has exceeded
459
 * LWS_LOG_SPEW_TS_RING / LWS_LOG_SPEW_ENTER_US, we enter spew mode: we
460
 * allocate a small heap ringbuffer of LWS_LOG_SPEW_RING_SIZE bytes and divert
461
 * the formatted lines into that instead of emitting them.  It retains only
462
 * the last LWS_LOG_SPEW_TAIL_LINES lines, fewer if they are long: enough to
463
 * see how the spew ended, since the spew itself is the problem, and this also
464
 * runs on devices with little heap.  A heartbeat line goes out once a second
465
 * so the log shows the process is alive and how much was swallowed.
466
 *
467
 * Leaving spew mode is decided over a much shorter window than entering it,
468
 * since a spew typically never pauses at all: once the last
469
 * LWS_LOG_SPEW_EXIT_SAMPLES lines span more than the exit quiet, initially
470
 * LWS_LOG_SPEW_EXIT_US, we call the spew over, replay the retained tail in
471
 * order, free the ringbuffer and go back to emitting directly.  The timestamp
472
 * ring is restarted, so a full LWS_LOG_SPEW_TS_RING lines at spew rate are
473
 * needed before we would enter again; that stops a bursty spew flapping in
474
 * and out of the mode and replaying its tail each time.
475
 *
476
 * When the next log arrives, a spew that stopped and a process that was
477
 * starved of cpu for a while, eg, on an overloaded box, look exactly the same.
478
 * What does tell them apart is the spew coming back: if we enter spew mode
479
 * again within LWS_LOG_SPEW_RESUME_US of calling it over, we were wrong, and
480
 * the quiet that fooled us is what a stall looks like on this box at the
481
 * moment.  So the exit quiet grows to twice that, up to
482
 * LWS_LOG_SPEW_EXIT_MAX_US, and we say so.  Conditions change, so each time a
483
 * spew starts afresh, the exit quiet halves back towards LWS_LOG_SPEW_EXIT_US.
484
 *
485
 * A legitimate surge of logs, eg, context creation at debug level, may be
486
 * fast enough to trip entry.  The first LWS_LOG_SPEW_TS_RING - 1 lines of it
487
 * were emitted before we entered spew mode, and if it ends within the tail we
488
 * retain, it is replayed intact, so a surge of up to that many lines is
489
 * waved through losslessly.  A longer one loses its middle, as a spew does.
490
 *
491
 * The exit check can only run when a log arrives.  If a spew stops dead and
492
 * nothing logs afterwards, the retained tail is replayed by the next log
493
 * whenever it comes, or by lws_context_destroy().  If the process dies
494
 * during a spew, the retained tail dies with it; that is the price of not
495
 * having written it out.
496
 *
497
 * The tunables can be overridden from the compiler command line.  How much of
498
 * the tail is retained can also be changed at runtime by
499
 * lws_log_spew_tail_lines(), eg from the --log-spew-tail commandline switch,
500
 * for when the part of interest is further back than the default tail.  A log
501
 * context with LLLF_LOG_SPEW_OFF in its flags bypasses all of this.
502
 */
503
504
#if !defined(LWS_LOG_SPEW_TS_RING)
505
584
#define LWS_LOG_SPEW_TS_RING    64
506
#endif
507
#if !defined(LWS_LOG_SPEW_ENTER_US)
508
2
#define LWS_LOG_SPEW_ENTER_US   20000
509
#endif
510
#if !defined(LWS_LOG_SPEW_EXIT_SAMPLES)
511
112
#define LWS_LOG_SPEW_EXIT_SAMPLES 8
512
#endif
513
#if !defined(LWS_LOG_SPEW_EXIT_US)
514
2
#define LWS_LOG_SPEW_EXIT_US    5000
515
#endif
516
#if !defined(LWS_LOG_SPEW_EXIT_MAX_US)
517
0
#define LWS_LOG_SPEW_EXIT_MAX_US  1000000
518
#endif
519
#if !defined(LWS_LOG_SPEW_RESUME_US)
520
0
#define LWS_LOG_SPEW_RESUME_US    1000000
521
#endif
522
#if !defined(LWS_LOG_SPEW_TAIL_LINES)
523
2
#define LWS_LOG_SPEW_TAIL_LINES   10
524
#endif
525
#if !defined(LWS_LOG_SPEW_RING_SIZE)
526
/* a threaded build's lines are 1024 long, too big for the small ring */
527
#if (defined(LWS_PLAT_FREERTOS) || defined(LWS_PLAT_BAREMETAL)) && \
528
    !defined(LWS_LOG_THREADED)
529
#define LWS_LOG_SPEW_RING_SIZE    1024
530
#else
531
1
#define LWS_LOG_SPEW_RING_SIZE    2048
532
#endif
533
#endif
534
#if !defined(LWS_LOG_SPEW_HEARTBEAT_US)
535
36
#define LWS_LOG_SPEW_HEARTBEAT_US 1000000
536
#endif
537
/*
538
 * Ring bytes budgeted per line when the tail is set at runtime: typical lines
539
 * fit, so the tail is usually the number of lines asked for, fewer if they
540
 * are long
541
 */
542
#if !defined(LWS_LOG_SPEW_LINE_BUDGET)
543
0
#define LWS_LOG_SPEW_LINE_BUDGET  256
544
#endif
545
/* limit of a runtime tail, so a typo cannot ask for gigabytes of heap */
546
#if !defined(LWS_LOG_SPEW_TAIL_LINES_MAX)
547
0
#define LWS_LOG_SPEW_TAIL_LINES_MAX 65536
548
#endif
549
550
/* each retained line is [len lo][len hi][level lo][level hi][len bytes] */
551
148
#define SPEW_HDR      4
552
553
#if LWS_LOG_LINE_MAX + SPEW_HDR > LWS_LOG_SPEW_RING_SIZE
554
#error "LWS_LOG_SPEW_RING_SIZE must hold at least one maximum-length log line"
555
#endif
556
557
typedef struct lws_log_spew_ring {
558
  uint8_t   *buf;   /* NULL: not in spew mode */
559
  size_t    size;   /* of buf */
560
  size_t    head;   /* next byte to write */
561
  size_t    tail;   /* oldest byte retained */
562
  size_t    used;
563
  unsigned int  lines;    /* retained */
564
  unsigned int  max_lines;  /* most lines to retain */
565
  lws_usec_t  entered;
566
  lws_usec_t  exit_us;  /* quiet needed to call this spew over */
567
  lws_usec_t  quiet;    /* the quiet that called it over */
568
  uint32_t  swallowed;  /* lines diverted into the ring */
569
  uint32_t  lost;   /* of those, pushed out again */
570
} lws_log_spew_ring_t;
571
572
typedef struct lws_log_spew {
573
  lws_usec_t    ts[LWS_LOG_SPEW_TS_RING];
574
  lws_log_spew_ring_t r;
575
  lws_usec_t    last_heartbeat;
576
  lws_usec_t    exit_us;  /* quiet that calls a spew over */
577
  lws_usec_t    exited;   /* when we last called one over */
578
  lws_usec_t    exited_quiet; /* ... on seeing this quiet */
579
  unsigned int    ts_head;  /* next slot to write */
580
  unsigned int    ts_count; /* valid slots */
581
  unsigned int    tail_lines; /* runtime tail, 0 = default */
582
} lws_log_spew_t;
583
584
static lws_log_spew_t spew;
585
static char spew_entering; /* setting the ring up: no re-entry */
586
587
enum {
588
  SPEW_EMIT,  /* not in spew mode, emit normally */
589
  SPEW_ENTERED, /* this line entered spew mode and was retained */
590
  SPEW_RESUMED, /* ... and we had called the spew over too early */
591
  SPEW_SWALLOWED, /* retained, say nothing */
592
  SPEW_HEARTBEAT, /* retained, but it is time to show signs of life */
593
  SPEW_EXITED /* spew eased: replay the ring, then emit normally */
594
};
595
596
/* timestamp of the line k lines before the most recent one */
597
598
static lws_usec_t
599
spew_ts_ago(unsigned int k)
600
38
{
601
38
  return spew.ts[(spew.ts_head + LWS_LOG_SPEW_TS_RING - 1 - k) %
602
38
           LWS_LOG_SPEW_TS_RING];
603
38
}
604
605
static void
606
spew_ring_write(lws_log_spew_ring_t *r, const uint8_t *p, size_t len)
607
74
{
608
74
  size_t n = r->size - r->head;
609
610
74
  if (n > len)
611
73
    n = len;
612
74
  memcpy(r->buf + r->head, p, n);
613
74
  if (len - n)
614
1
    memcpy(r->buf, p + n, len - n);
615
74
  r->head = (r->head + len) % r->size;
616
74
  r->used += len;
617
74
}
618
619
static void
620
spew_ring_read(lws_log_spew_ring_t *r, uint8_t *p, size_t len)
621
47
{
622
47
  size_t n = r->size - r->tail;
623
624
47
  if (n > len)
625
47
    n = len;
626
47
  memcpy(p, r->buf + r->tail, n);
627
47
  if (len - n)
628
0
    memcpy(p + n, r->buf, len - n);
629
47
  r->tail = (r->tail + len) % r->size;
630
47
  r->used -= len;
631
47
}
632
633
/* take the oldest retained line out of the ring, 0 if nothing retained */
634
635
static size_t
636
spew_ring_pop(lws_log_spew_ring_t *r, char *line, size_t max, int *level)
637
37
{
638
37
  uint8_t hdr[SPEW_HDR];
639
37
  size_t len;
640
641
37
  *level = 0;
642
37
  if (!r->lines)
643
0
    return 0;
644
645
37
  spew_ring_read(r, hdr, SPEW_HDR);
646
37
  len = (size_t)hdr[0] | ((size_t)hdr[1] << 8);
647
37
  *level = hdr[2] | (hdr[3] << 8);
648
37
  r->lines--;
649
650
37
  if (!line) {
651
    /* discard it */
652
27
    r->tail = (r->tail + len) % r->size;
653
27
    r->used -= len;
654
655
27
    return len;
656
27
  }
657
658
37
  assert(len < max);
659
10
  spew_ring_read(r, (uint8_t *)line, len);
660
10
  line[len] = '\0';
661
662
10
  return len;
663
10
}
664
665
static void
666
spew_ring_push(lws_log_spew_ring_t *r, int level, const char *line, size_t len)
667
37
{
668
37
  uint8_t hdr[SPEW_HDR];
669
37
  int lv;
670
671
  /*
672
   * len is at most LWS_LOG_LINE_MAX, which the ring is checked at
673
   * compile time to hold along with its header, and is never sized
674
   * smaller than that at runtime.  Still hold that against the ring we
675
   * actually have: a line it can't hold would never fit however many
676
   * old lines we forget below, and would overrun it if written.
677
   */
678
679
37
  if (len + SPEW_HDR > r->size)
680
0
    return;
681
682
  /* make room by forgetting the oldest lines */
683
684
64
  while (r->lines >= r->max_lines ||
685
37
         r->size - r->used < len + SPEW_HDR) {
686
27
    spew_ring_pop(r, NULL, 0, &lv);
687
27
    r->lost++;
688
27
  }
689
690
37
  hdr[0] = (uint8_t)(len & 0xff);
691
37
  hdr[1] = (uint8_t)(len >> 8);
692
37
  hdr[2] = (uint8_t)(level & 0xff);
693
37
  hdr[3] = (uint8_t)((level >> 8) & 0xff);
694
37
  spew_ring_write(r, hdr, SPEW_HDR);
695
37
  spew_ring_write(r, (const uint8_t *)line, len);
696
37
  r->lines++;
697
37
  r->swallowed++;
698
37
}
699
700
/*
701
 * Account for a line that is about to be emitted and decide its fate.
702
 *
703
 * Called with the log lock held.  If we leave spew mode, ownership of the ring
704
 * is handed out via *replay so the caller can replay and free it without the
705
 * lock, and the caller sees an empty ring here from then on.
706
 */
707
708
static int
709
spew_track(lws_usec_t now, int level, const char *line, size_t len,
710
     lws_log_spew_ring_t *replay)
711
145
{
712
145
  lws_usec_t quiet;
713
145
  int act;
714
715
145
  spew.ts[spew.ts_head] = now;
716
145
  spew.ts_head = (spew.ts_head + 1) % LWS_LOG_SPEW_TS_RING;
717
145
  if (spew.ts_count < LWS_LOG_SPEW_TS_RING)
718
108
    spew.ts_count++;
719
720
145
  if (!spew.r.buf) {
721
108
    if (spew.ts_count < LWS_LOG_SPEW_TS_RING ||
722
1
        now - spew_ts_ago(LWS_LOG_SPEW_TS_RING - 1) >=
723
1
            LWS_LOG_SPEW_ENTER_US)
724
107
      return SPEW_EMIT;
725
726
    /*
727
     * The ring comes from the libc allocator, not lws_malloc():
728
     * the lws allocator logs its allocations at debug level, and
729
     * that log re-entered here while the ring was being set up,
730
     * so an inner call allocated and filled one ring and the
731
     * outer then installed a different buffer under the inner's
732
     * counters (and with a non-recursive log lock it would have
733
     * deadlocked).  The guard below refuses re-entry outright.
734
     */
735
1
    if (spew_entering)
736
0
      return SPEW_EMIT;
737
1
    spew_entering = 1;
738
1
    memset(&spew.r, 0, sizeof(spew.r));
739
1
    spew.r.max_lines = LWS_LOG_SPEW_TAIL_LINES;
740
1
    spew.r.size = LWS_LOG_SPEW_RING_SIZE;
741
1
    if (spew.tail_lines) {
742
0
      spew.r.max_lines = spew.tail_lines;
743
0
      if (spew.r.size < (size_t)spew.tail_lines *
744
0
            LWS_LOG_SPEW_LINE_BUDGET)
745
0
        spew.r.size = (size_t)spew.tail_lines *
746
0
            LWS_LOG_SPEW_LINE_BUDGET;
747
0
    }
748
    /* zeroed, so no stale heap content can ever be read out of it */
749
1
    spew.r.buf = calloc(1, spew.r.size);
750
1
    spew_entering = 0;
751
1
    if (!spew.r.buf)
752
      /* no memory to retain anything: keep emitting */
753
0
      return SPEW_EMIT;
754
755
1
    if (spew.exited && now - spew.exited < LWS_LOG_SPEW_RESUME_US) {
756
      /*
757
       * The spew never ended, we were only descheduled for
758
       * long enough to think so: don't be fooled by that
759
       * again
760
       */
761
0
      act = SPEW_RESUMED;
762
0
      if (spew.exit_us < 2 * spew.exited_quiet)
763
0
        spew.exit_us = 2 * spew.exited_quiet;
764
0
      if (spew.exit_us > LWS_LOG_SPEW_EXIT_MAX_US)
765
0
        spew.exit_us = LWS_LOG_SPEW_EXIT_MAX_US;
766
0
      spew.r.quiet = spew.exited_quiet;
767
1
    } else {
768
      /* a new spew: conditions may have improved since */
769
1
      act = SPEW_ENTERED;
770
1
      spew.exit_us /= 2;
771
1
      if (spew.exit_us < LWS_LOG_SPEW_EXIT_US)
772
1
        spew.exit_us = LWS_LOG_SPEW_EXIT_US;
773
1
    }
774
1
    spew.exited = 0;
775
776
1
    spew.r.entered = now;
777
1
    spew.r.exit_us = spew.exit_us;
778
1
    spew.last_heartbeat = now;
779
1
    spew_ring_push(&spew.r, level, line, len);
780
1
    *replay = spew.r;
781
782
1
    return act;
783
1
  }
784
785
37
  quiet = now - spew_ts_ago(LWS_LOG_SPEW_EXIT_SAMPLES);
786
37
  if (spew.ts_count > LWS_LOG_SPEW_EXIT_SAMPLES &&
787
37
      quiet > spew.r.exit_us) {
788
1
    spew.r.quiet = quiet;
789
1
    *replay = spew.r;
790
1
    memset(&spew.r, 0, sizeof(spew.r));
791
    /* a fresh run at spew rate is needed to enter again */
792
1
    spew.ts_head = 0;
793
1
    spew.ts_count = 0;
794
    /* if it enters again right away, we were wrong about this */
795
1
    spew.exited = now;
796
1
    spew.exited_quiet = quiet;
797
798
1
    return SPEW_EXITED;
799
1
  }
800
801
36
  spew_ring_push(&spew.r, level, line, len);
802
803
36
  if (now - spew.last_heartbeat >= LWS_LOG_SPEW_HEARTBEAT_US) {
804
0
    spew.last_heartbeat = now;
805
0
    *replay = spew.r;
806
807
0
    return SPEW_HEARTBEAT;
808
0
  }
809
810
36
  return SPEW_SWALLOWED;
811
36
}
812
813
static void
814
log_emit(lws_log_cx_t *cx, int level, const char *line, size_t len)
815
121
{
816
121
  if (cx->lll_flags & LLLF_LOG_CONTEXT_AWARE)
817
0
    cx->u.emit_cx(cx, level, line, len);
818
121
  else
819
121
    cx->u.emit(level, line);
820
121
}
821
822
/* emit a line of our own about the spew, in the style of the cx */
823
824
static void LWS_FORMAT(3)
825
spew_emit(lws_log_cx_t *cx, int level, const char *format, ...)
826
3
{
827
3
  char b[160], *p = b, *end = b + sizeof(b) - 2;
828
3
  va_list ap;
829
3
  int n;
830
831
3
  b[0] = '\0';
832
#if !defined(LWS_LOGS_TIMESTAMP)
833
  if (cx->lll_flags & LLLF_LOG_TIMESTAMP)
834
#endif
835
3
  {
836
3
    lwsl_timestamp(level, b, sizeof(b));
837
3
    p += strlen(b);
838
3
  }
839
840
3
  va_start(ap, format);
841
3
  n = vsnprintf(p, lws_ptr_diff_size_t(end, p), format, ap);
842
3
  va_end(ap);
843
3
  if (n < 0)
844
0
    n = 0;
845
3
  p += n;
846
3
  if (p > end)
847
0
    p = end;
848
3
  *p++ = '\n';
849
3
  *p = '\0';
850
851
3
  log_emit(cx, level, b, lws_ptr_diff_size_t(p, b));
852
3
}
853
854
/* replay the retained tail of a spew in order, then free the ring */
855
856
static void
857
spew_replay(lws_log_cx_t *cx, int level, lws_log_spew_ring_t *r,
858
      lws_usec_t now)
859
1
{
860
1
  char line[LWS_LOG_LINE_MAX + 1];
861
1
  int lv = level;
862
1
  size_t len;
863
864
1
  spew_emit(cx, level, "lws: log spew eased: %ums quiet, swallowed %u logs"
865
1
           " over %ums, %u oldest not retained, last %u"
866
1
           " replayed:",
867
1
      (unsigned int)(r->quiet / 1000),
868
1
      (unsigned int)r->swallowed,
869
1
      (unsigned int)((now - r->entered) / 1000),
870
1
      (unsigned int)r->lost, r->lines);
871
872
11
  while (r->lines) {
873
10
    len = spew_ring_pop(r, line, sizeof(line), &lv);
874
10
    log_emit(cx, lv, line, len);
875
10
  }
876
877
1
  spew_emit(cx, level, "lws: log spew: end of replay");
878
879
1
  free(r->buf); /* libc's: see spew_track() */
880
1
  r->buf = NULL;
881
1
}
882
883
void
884
lws_log_spew_tail_lines(unsigned int lines)
885
0
{
886
0
  if (lines > LWS_LOG_SPEW_TAIL_LINES_MAX)
887
0
    lines = LWS_LOG_SPEW_TAIL_LINES_MAX;
888
889
  /* a spew in progress keeps the ring it has, the next one uses this */
890
0
  log_lock_take();
891
0
  spew.tail_lines = lines;
892
0
  log_lock_release();
893
0
}
894
895
/*
896
 * If a spew was in progress, replay and free what it retained now, rather than
897
 * waiting for a log that may never come.  Called from lws_context_destroy().
898
 */
899
900
void
901
lws_log_spew_flush(lws_log_cx_t *cx)
902
0
{
903
0
  lws_log_spew_ring_t r;
904
905
0
  if (!cx)
906
0
    cx = &log_cx;
907
908
0
  log_lock_take();
909
0
  r = spew.r;
910
0
  memset(&spew.r, 0, sizeof(spew.r));
911
0
  spew.ts_head = 0;
912
0
  spew.ts_count = 0;
913
  /* not called over by a quiet: nothing to learn if it comes back */
914
0
  spew.exited = 0;
915
0
  log_lock_release();
916
917
0
  if (r.buf)
918
0
    spew_replay(cx, LLL_NOTICE, &r, lws_now_usecs());
919
0
}
920
921
void
922
__lws_logv(lws_log_cx_t *cx, lws_log_prepend_cx_t prep, void *obj,
923
     int filter, uint32_t dropped, const char *_fun, const char *format, va_list vl)
924
11.8k
{
925
#if !defined(LWS_LOG_THREADED)
926
  /* this is incompatible with multithreaded logging */
927
  static char buf[LWS_LOG_LINE_MAX];
928
#else
929
11.8k
  char buf[LWS_LOG_LINE_MAX];
930
11.8k
#endif
931
11.8k
  static char prev_buf[LWS_LOG_LINE_MAX];
932
11.8k
  static uint32_t log_dupes;
933
11.8k
  static lws_usec_t last_log_dupe_emit;
934
11.8k
  char *p = buf, *end = p + sizeof(buf) - 1, *body_start;
935
11.8k
  lws_log_spew_ring_t replay;
936
11.8k
  lws_log_cx_t *cxp;
937
11.8k
  int n, back = 0, act = SPEW_EMIT;
938
11.8k
  lws_usec_t now;
939
940
  /*
941
   * We need to handle NULL wsi etc at the wrappers as gracefully as
942
   * possible
943
   */
944
945
11.8k
  if (!cx) {
946
0
    lws_strncpy(p, "NULL log cx: ", sizeof(buf) - 1);
947
0
    p += 13;
948
    /* use the processwide one for lack of anything better */
949
0
    cx = &log_cx;
950
0
  }
951
952
11.8k
  cxp = cx;
953
954
11.8k
  if (!(cx->lll_flags & (uint32_t)filter))
955
    /*
956
     * logs may be produced and built in to the code but disabled
957
     * at runtime
958
     */
959
11.6k
    return;
960
961
#if !defined(LWS_LOGS_TIMESTAMP)
962
  if (cx->lll_flags & LLLF_LOG_TIMESTAMP)
963
#endif
964
182
  {
965
182
    buf[0] = '\0';
966
182
    lwsl_timestamp(filter, buf, sizeof(buf));
967
182
    p += strlen(buf);
968
182
  }
969
970
182
  body_start = p;
971
972
  /*
973
   * prepend parent log ctx content first
974
   * top level cx also gets an opportunity to prepend
975
   */
976
977
182
  while (cxp->parent) {
978
0
    cxp = cxp->parent;
979
0
    back++;
980
0
  }
981
982
182
  do {
983
182
    int b = back;
984
985
182
    cxp = cx;
986
182
    while (b--)
987
0
      cxp = cxp->parent;
988
182
    if (cxp->prepend)
989
0
      cxp->prepend(cxp, NULL, &p, end);
990
991
182
    back--;
992
182
  } while (back > 0);
993
994
182
  if (prep)
995
0
    prep(cxp, obj, &p, end);
996
997
182
  if (_fun)
998
0
    p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "%s: ", _fun);
999
1000
  /*
1001
   * The actual log content
1002
   */
1003
1004
182
  n = vsnprintf(p, lws_ptr_diff_size_t(end, p), format, vl);
1005
1006
  /* vnsprintf returns what it would have written, even if truncated */
1007
182
  if (p + n > end - 2) {
1008
0
    p = end - 5;
1009
0
    *p++ = '.';
1010
0
    *p++ = '.';
1011
0
    *p++ = '.';
1012
0
    *p++ = '\n';
1013
0
    *p++ = '\0';
1014
182
  } else {
1015
182
    if (n > 0) {
1016
182
      p += n;
1017
182
      if (p[-1] == '\n')
1018
182
        p--;
1019
182
      if (dropped > 1)
1020
0
        p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), " (dropped %u logs)", (unsigned int)(dropped - 1));
1021
182
      if (p < end - 1) {
1022
182
        *p++ = '\n';
1023
182
        *p = '\0';
1024
182
      }
1025
182
    }
1026
182
  }
1027
1028
182
  now = lws_now_usecs();
1029
1030
182
  log_lock_take();
1031
1032
182
  if (!strcmp(body_start, prev_buf)) {
1033
37
    log_dupes++;
1034
37
    if (now - last_log_dupe_emit < 1000000) {
1035
37
      log_lock_release();
1036
37
      return;
1037
37
    }
1038
1039
0
    p = body_start + strlen(body_start);
1040
0
    if (p > buf && p[-1] == '\n')
1041
0
      p--;
1042
0
    p += lws_snprintf(p, lws_ptr_diff_size_t(end, p),
1043
0
          " (swallowed %u dupes)\n", (unsigned int)log_dupes);
1044
0
    log_dupes = 0;
1045
0
    last_log_dupe_emit = now;
1046
145
  } else {
1047
145
    lws_strncpy(prev_buf, body_start, sizeof(prev_buf));
1048
145
    log_dupes = 0;
1049
145
    last_log_dupe_emit = now;
1050
145
  }
1051
1052
145
  if (!(cx->lll_flags & LLLF_LOG_SPEW_OFF))
1053
145
    act = spew_track(now, filter, buf, lws_ptr_diff_size_t(p, buf),
1054
145
         &replay);
1055
1056
145
  log_lock_release();
1057
1058
145
  switch (act) {
1059
1
  case SPEW_ENTERED:
1060
1
    spew_emit(cx, filter, "lws: log spew: %u logs in %ums, retaining"
1061
1
              " the last %u of it until %u logs"
1062
1
              " span over %ums",
1063
1
        LWS_LOG_SPEW_TS_RING, LWS_LOG_SPEW_ENTER_US / 1000,
1064
1
        LWS_LOG_SPEW_TAIL_LINES, LWS_LOG_SPEW_EXIT_SAMPLES,
1065
1
        (unsigned int)(replay.exit_us / 1000));
1066
1
    return;
1067
1068
0
  case SPEW_RESUMED:
1069
0
    spew_emit(cx, filter, "lws: log spew: resumed after %ums quiet,"
1070
0
              " it was not over: retaining again until"
1071
0
              " %u logs span over %ums",
1072
0
        (unsigned int)(replay.quiet / 1000),
1073
0
        LWS_LOG_SPEW_EXIT_SAMPLES,
1074
0
        (unsigned int)(replay.exit_us / 1000));
1075
0
    return;
1076
1077
36
  case SPEW_SWALLOWED:
1078
36
    return;
1079
1080
0
  case SPEW_HEARTBEAT:
1081
0
    spew_emit(cx, filter, "lws: log spew: still going, %u logs"
1082
0
              " swallowed over %us",
1083
0
        (unsigned int)replay.swallowed,
1084
0
        (unsigned int)((now - replay.entered) / 1000000));
1085
0
    return;
1086
1087
1
  case SPEW_EXITED:
1088
1
    spew_replay(cx, filter, &replay, now);
1089
1
    break;
1090
1091
107
  default:
1092
107
    break;
1093
145
  }
1094
1095
  /*
1096
   * The actual emit
1097
   */
1098
1099
108
  log_emit(cx, filter, buf, lws_ptr_diff_size_t(p, buf));
1100
108
}
1101
1102
void _lws_logv(int filter, const char *format, va_list vl)
1103
0
{
1104
0
  __lws_logv(&log_cx, NULL, NULL, filter, 0, NULL, format, vl);
1105
0
}
1106
1107
void _lws_log(int filter, const char *format, ...)
1108
11.8k
{
1109
11.8k
  va_list ap;
1110
1111
11.8k
  va_start(ap, format);
1112
11.8k
  __lws_logv(&log_cx, NULL, NULL, filter, 0, NULL, format, ap);
1113
11.8k
  va_end(ap);
1114
11.8k
}
1115
1116
void _lws_log_rl(int filter, uint32_t dropped, const char *format, ...)
1117
0
{
1118
0
  va_list ap;
1119
1120
0
  va_start(ap, format);
1121
0
  __lws_logv(&log_cx, NULL, NULL, filter, dropped, NULL, format, ap);
1122
0
  va_end(ap);
1123
0
}
1124
1125
void _lws_log_cx(lws_log_cx_t *cx, lws_log_prepend_cx_t prep, void *obj,
1126
     int filter, const char *_fun, const char *format, ...)
1127
0
{
1128
0
  va_list ap;
1129
1130
0
  if (!cx)
1131
0
    cx = &log_cx;
1132
1133
0
  va_start(ap, format);
1134
0
  __lws_logv(cx, prep, obj, filter, 0, _fun, format, ap);
1135
0
  va_end(ap);
1136
0
}
1137
1138
void _lws_log_cx_rl(lws_log_cx_t *cx, lws_log_prepend_cx_t prep, void *obj,
1139
     int filter, uint32_t dropped, const char *_fun, const char *format, ...)
1140
0
{
1141
0
  va_list ap;
1142
1143
0
  if (!cx)
1144
0
    cx = &log_cx;
1145
1146
0
  va_start(ap, format);
1147
0
  __lws_logv(cx, prep, obj, filter, dropped, _fun, format, ap);
1148
0
  va_end(ap);
1149
0
}
1150
#endif
1151
1152
void
1153
lws_set_log_level(int flags, lws_log_emit_t func)
1154
0
{
1155
0
  log_cx.lll_flags = (uint32_t)(flags & (~LLLF_LOG_CONTEXT_AWARE));
1156
1157
0
  if (func)
1158
0
    log_cx.u.emit = func;
1159
0
}
1160
1161
int lwsl_visible(int level)
1162
0
{
1163
0
  return !!(log_cx.lll_flags & (uint32_t)level);
1164
0
}
1165
1166
int lwsl_visible_cx(lws_log_cx_t *cx, int level)
1167
0
{
1168
0
  return !!(cx->lll_flags & (uint32_t)level);
1169
0
}
1170
1171
void
1172
lwsl_refcount_cx(lws_log_cx_t *cx, int _new)
1173
0
{
1174
#if LWS_MAX_SMP > 1
1175
  volatile lws_log_cx_t *vcx = (volatile lws_log_cx_t *)cx;
1176
#endif
1177
1178
0
  if (!cx)
1179
0
    return;
1180
1181
#if LWS_MAX_SMP > 1
1182
  if (!vcx->inited) {
1183
    vcx->inited = 1;
1184
    lws_pthread_mutex_init(&cx->refcount_lock);
1185
    vcx->inited = 2;
1186
  }
1187
  while (vcx->inited != 2)
1188
    ;
1189
  lws_pthread_mutex_lock(&cx->refcount_lock);
1190
#endif
1191
1192
0
  if (_new > 0)
1193
0
    cx->refcount++;
1194
0
  else {
1195
0
    assert(cx->refcount);
1196
0
    cx->refcount--;
1197
0
  }
1198
1199
0
  if (cx->refcount_cb)
1200
0
    cx->refcount_cb(cx, _new);
1201
1202
#if LWS_MAX_SMP > 1
1203
  lws_pthread_mutex_unlock(&cx->refcount_lock);
1204
#endif
1205
0
}
1206
1207
void
1208
lwsl_hexdump_level_cx(lws_log_cx_t *cx, lws_log_prepend_cx_t prep, void *obj,
1209
          int hexdump_level, const void *vbuf, size_t len)
1210
0
{
1211
0
  unsigned char *buf = (unsigned char *)vbuf;
1212
0
  unsigned int n;
1213
1214
0
  if (!lwsl_visible_cx(cx, hexdump_level))
1215
0
    return;
1216
1217
0
  if (!len) {
1218
0
    _lws_log_cx(cx, prep, obj, hexdump_level, NULL,
1219
0
          "(hexdump: zero length)\n");
1220
0
    return;
1221
0
  }
1222
1223
0
  if (!vbuf) {
1224
0
    _lws_log_cx(cx, prep, obj, hexdump_level, NULL,
1225
0
          "(hexdump: NULL ptr)\n");
1226
0
    return;
1227
0
  }
1228
1229
0
  _lws_log_cx(cx, prep, obj, hexdump_level, NULL, "\n");
1230
1231
0
  for (n = 0; n < len;) {
1232
0
    unsigned int start = n, m;
1233
0
    char line[80], *p = line;
1234
1235
0
    p += lws_snprintf(p, 10, "%04X: ", start);
1236
1237
0
    for (m = 0; m < 16 && n < len; m++)
1238
0
      p += lws_snprintf(p, 5, "%02X ", buf[n++]);
1239
0
    while (m++ < 16)
1240
0
      p += lws_snprintf(p, 5, "   ");
1241
1242
0
    p += lws_snprintf(p, 6, "   ");
1243
1244
0
    for (m = 0; m < 16 && (start + m) < len; m++) {
1245
0
      if (buf[start + m] >= ' ' && buf[start + m] < 127)
1246
0
        *p++ = (char)buf[start + m];
1247
0
      else
1248
0
        *p++ = '.';
1249
0
    }
1250
0
    while (m++ < 16)
1251
0
      *p++ = ' ';
1252
1253
0
    *p++ = '\n';
1254
0
    *p = '\0';
1255
0
    _lws_log_cx(cx, prep, obj, hexdump_level, NULL, "%s", line);
1256
0
    (void)line;
1257
0
  }
1258
1259
0
  _lws_log_cx(cx, prep, obj, hexdump_level, NULL, "\n");
1260
0
}
1261
1262
void
1263
lwsl_hexdump_level(int hexdump_level, const void *vbuf, size_t len)
1264
0
{
1265
0
  lwsl_hexdump_level_cx(&log_cx, NULL, NULL, hexdump_level, vbuf, len);
1266
0
}
1267
1268
void
1269
lwsl_hexdump(const void *vbuf, size_t len)
1270
0
{
1271
0
#if defined(_DEBUG)
1272
0
  lwsl_hexdump_level(LLL_DEBUG, vbuf, len);
1273
0
#endif
1274
0
}
1275
1276
/*
1277
 * A context's log lines: its log context, and its name to prefix them with
1278
 */
1279
1280
void
1281
lws_log_prepend_context(struct lws_log_cx *cx, void *obj, char **p, char *e)
1282
0
{
1283
0
  struct lws_context *lcx = (struct lws_context *)obj;
1284
1285
0
  if (lcx->name)
1286
0
    *p += lws_snprintf(*p, lws_ptr_diff_size_t(e, (*p)), "%s: ",
1287
0
           lcx->name);
1288
0
}
1289
1290
struct lws_log_cx *
1291
lwsl_context_get_cx(struct lws_context *cx)
1292
0
{
1293
0
  if (!cx)
1294
0
    return NULL;
1295
1296
0
  return cx->log_cx;
1297
0
}