Coverage Report

Created: 2026-09-14 06:24

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
  .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
}