blob: e183d01cbdaa7c089b0d5b2b69d2f0cb49e638c6 [file] [log] [blame]
Pekka Paalanena5630ea2017-10-12 13:13:42 +02001/*
2 * Copyright © 2017 Pekka Paalanen <pq@iki.fi>
3 * Copyright © 2018 Zodiac Inflight Innovations
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the
14 * next paragraph) shall be included in all copies or substantial
15 * portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 * SOFTWARE.
25 */
26
27#include "config.h"
28
29#include "weston-debug.h"
30#include "helpers.h"
Pekka Paalanen3d5d9472019-03-28 16:28:47 +020031#include <libweston/libweston.h>
Pekka Paalanena5630ea2017-10-12 13:13:42 +020032
33#include "weston-debug-server-protocol.h"
34
35#include <assert.h>
36#include <unistd.h>
37#include <stdarg.h>
38#include <string.h>
39#include <errno.h>
40#include <sys/time.h>
41
42/** Main weston-debug context
43 *
44 * One per weston_compositor.
45 *
46 * \internal
47 */
48struct weston_debug_compositor {
49 struct weston_compositor *compositor;
50 struct wl_listener compositor_destroy_listener;
51 struct wl_global *global;
52 struct wl_list scope_list; /**< weston_debug_scope::compositor_link */
53};
54
55/** weston-debug message scope
56 *
57 * This is used for scoping debugging messages. Clients can subscribe to
58 * only the scopes they are interested in. A scope is identified by its name
59 * (also referred to as debug stream name).
60 */
61struct weston_debug_scope {
62 char *name;
63 char *desc;
64 weston_debug_scope_cb begin_cb;
65 void *user_data;
66 struct wl_list stream_list; /**< weston_debug_stream::scope_link */
67 struct wl_list compositor_link;
68};
69
70/** A debug stream created by a client
71 *
72 * A client provides a file descriptor for the server to write debug
73 * messages into. A weston_debug_stream is associated to one
74 * weston_debug_scope via the scope name, and the scope provides the messages.
75 * There can be several streams for the same scope, all streams getting the
76 * same messages.
77 */
78struct weston_debug_stream {
79 int fd; /**< client provided fd */
80 struct wl_resource *resource; /**< weston_debug_stream_v1 object */
81 struct wl_list scope_link;
82};
83
84static struct weston_debug_scope *
85get_scope(struct weston_debug_compositor *wdc, const char *name)
86{
87 struct weston_debug_scope *scope;
88
89 wl_list_for_each(scope, &wdc->scope_list, compositor_link)
90 if (strcmp(name, scope->name) == 0)
91 return scope;
92
93 return NULL;
94}
95
96static void
97stream_close_unlink(struct weston_debug_stream *stream)
98{
99 if (stream->fd != -1)
100 close(stream->fd);
101 stream->fd = -1;
102
103 wl_list_remove(&stream->scope_link);
104 wl_list_init(&stream->scope_link);
105}
106
107static void WL_PRINTF(2, 3)
108stream_close_on_failure(struct weston_debug_stream *stream,
109 const char *fmt, ...)
110{
111 char *msg;
112 va_list ap;
113 int ret;
114
115 stream_close_unlink(stream);
116
117 va_start(ap, fmt);
118 ret = vasprintf(&msg, fmt, ap);
119 va_end(ap);
120
121 if (ret > 0) {
122 weston_debug_stream_v1_send_failure(stream->resource, msg);
123 free(msg);
124 } else {
125 weston_debug_stream_v1_send_failure(stream->resource,
126 "MEMFAIL");
127 }
128}
129
130static struct weston_debug_stream *
131stream_create(struct weston_debug_compositor *wdc, const char *name,
132 int32_t streamfd, struct wl_resource *stream_resource)
133{
134 struct weston_debug_stream *stream;
135 struct weston_debug_scope *scope;
136
137 stream = zalloc(sizeof *stream);
138 if (!stream)
139 return NULL;
140
141 stream->fd = streamfd;
142 stream->resource = stream_resource;
143
144 scope = get_scope(wdc, name);
145 if (scope) {
146 wl_list_insert(&scope->stream_list, &stream->scope_link);
147
148 if (scope->begin_cb)
149 scope->begin_cb(stream, scope->user_data);
150 } else {
151 wl_list_init(&stream->scope_link);
152 stream_close_on_failure(stream,
153 "Debug stream name '%s' is unknown.",
154 name);
155 }
156
157 return stream;
158}
159
160static void
161stream_destroy(struct wl_resource *stream_resource)
162{
163 struct weston_debug_stream *stream;
164
165 stream = wl_resource_get_user_data(stream_resource);
166
167 if (stream->fd != -1)
168 close(stream->fd);
169 wl_list_remove(&stream->scope_link);
170 free(stream);
171}
172
173static void
174weston_debug_stream_destroy(struct wl_client *client,
175 struct wl_resource *stream_resource)
176{
177 wl_resource_destroy(stream_resource);
178}
179
180static const struct weston_debug_stream_v1_interface
181 weston_debug_stream_impl = {
182 weston_debug_stream_destroy
183};
184
185static void
186weston_debug_destroy(struct wl_client *client,
187 struct wl_resource *global_resource)
188{
189 wl_resource_destroy(global_resource);
190}
191
192static void
193weston_debug_subscribe(struct wl_client *client,
194 struct wl_resource *global_resource,
195 const char *name,
196 int32_t streamfd,
197 uint32_t new_stream_id)
198{
199 struct weston_debug_compositor *wdc;
200 struct wl_resource *stream_resource;
201 uint32_t version;
202 struct weston_debug_stream *stream;
203
204 wdc = wl_resource_get_user_data(global_resource);
205 version = wl_resource_get_version(global_resource);
206
207 stream_resource = wl_resource_create(client,
208 &weston_debug_stream_v1_interface,
209 version, new_stream_id);
210 if (!stream_resource)
211 goto fail;
212
213 stream = stream_create(wdc, name, streamfd, stream_resource);
214 if (!stream)
215 goto fail;
216
217 wl_resource_set_implementation(stream_resource,
218 &weston_debug_stream_impl,
219 stream, stream_destroy);
220 return;
221
222fail:
223 close(streamfd);
224 wl_client_post_no_memory(client);
225}
226
227static const struct weston_debug_v1_interface weston_debug_impl = {
228 weston_debug_destroy,
229 weston_debug_subscribe
230};
231
232static void
233bind_weston_debug(struct wl_client *client,
234 void *data, uint32_t version, uint32_t id)
235{
236 struct weston_debug_compositor *wdc = data;
237 struct weston_debug_scope *scope;
238 struct wl_resource *resource;
239
240 resource = wl_resource_create(client,
241 &weston_debug_v1_interface,
242 version, id);
243 if (!resource) {
244 wl_client_post_no_memory(client);
245 return;
246 }
247 wl_resource_set_implementation(resource, &weston_debug_impl,
248 wdc, NULL);
249
250 wl_list_for_each(scope, &wdc->scope_list, compositor_link) {
251 weston_debug_v1_send_available(resource, scope->name,
252 scope->desc);
253 }
254}
255
Marius Vlad880b4852019-04-07 17:07:58 +0300256/**
257 * Connect weston-compositor structure to weston-debug structure
258 * an vice versa.
259 *
260 * \param compositor
261 * \param wdc
262 * \return 0 on success, -1 on failure
263 *
264 */
265int
266weston_debug_compositor_setup(struct weston_compositor *compositor,
267 struct weston_debug_compositor *wdc)
268{
269 if (compositor->weston_debug)
270 return -1;
271
272 wdc->compositor = compositor;
273 compositor->weston_debug = wdc;
274
275 return 0;
276}
277
Pekka Paalanena5630ea2017-10-12 13:13:42 +0200278/** Initialize weston-debug structure
279 *
280 * \param compositor The libweston compositor.
281 * \return 0 on success, -1 on failure.
282 *
283 * weston_debug_compositor is a singleton for each weston_compositor.
284 *
285 * Sets weston_compositor::weston_debug.
286 *
287 * \internal
288 */
Marius Vlad880b4852019-04-07 17:07:58 +0300289WL_EXPORT struct weston_debug_compositor *
290weston_debug_compositor_create(void)
Pekka Paalanena5630ea2017-10-12 13:13:42 +0200291{
292 struct weston_debug_compositor *wdc;
293
Pekka Paalanena5630ea2017-10-12 13:13:42 +0200294 wdc = zalloc(sizeof *wdc);
295 if (!wdc)
Marius Vlad880b4852019-04-07 17:07:58 +0300296 return NULL;
Pekka Paalanena5630ea2017-10-12 13:13:42 +0200297
Pekka Paalanena5630ea2017-10-12 13:13:42 +0200298 wl_list_init(&wdc->scope_list);
299
Marius Vlad880b4852019-04-07 17:07:58 +0300300 return wdc;
Pekka Paalanena5630ea2017-10-12 13:13:42 +0200301}
302
303/** Destroy weston_debug_compositor structure
304 *
305 * \param compositor The libweston compositor whose weston-debug to tear down.
306 *
307 * Clears weston_compositor::weston_debug.
308 *
309 * \internal
310 */
311void
312weston_debug_compositor_destroy(struct weston_compositor *compositor)
313{
314 struct weston_debug_compositor *wdc = compositor->weston_debug;
315 struct weston_debug_scope *scope;
316
317 if (wdc->global)
318 wl_global_destroy(wdc->global);
319
320 wl_list_for_each(scope, &wdc->scope_list, compositor_link)
321 weston_log("Internal warning: debug scope '%s' has not been destroyed.\n",
322 scope->name);
323
324 /* Remove head to not crash if scope removed later. */
325 wl_list_remove(&wdc->scope_list);
326
327 free(wdc);
328
329 compositor->weston_debug = NULL;
330}
331
332/** Enable weston-debug protocol extension
333 *
334 * \param compositor The libweston compositor where to enable.
335 *
336 * This enables the weston_debug_v1 Wayland protocol extension which any client
Emmanuel Gil Peyrot426c2462019-02-20 16:33:32 +0100337 * can use to get debug messages from the compositor.
Pekka Paalanena5630ea2017-10-12 13:13:42 +0200338 *
339 * WARNING: This feature should not be used in production. If a client
340 * provides a file descriptor that blocks writes, it will block the whole
341 * compositor indefinitely.
342 *
343 * There is no control on which client is allowed to subscribe to debug
344 * messages. Any and all clients are allowed.
345 *
346 * The debug extension is disabled by default, and once enabled, cannot be
347 * disabled again.
348 */
349WL_EXPORT void
350weston_compositor_enable_debug_protocol(struct weston_compositor *compositor)
351{
Marius Vlad1e2fda22019-04-07 19:07:16 +0300352 struct weston_log_context *log_ctx = compositor->weston_log_ctx;
353 assert(log_ctx);
354 if (log_ctx->global)
Pekka Paalanena5630ea2017-10-12 13:13:42 +0200355 return;
356
Marius Vlad1e2fda22019-04-07 19:07:16 +0300357 log_ctx->global = wl_global_create(compositor->wl_display,
Pekka Paalanena5630ea2017-10-12 13:13:42 +0200358 &weston_debug_v1_interface, 1,
Marius Vlad1e2fda22019-04-07 19:07:16 +0300359 log_ctx, bind_weston_debug);
360 if (!log_ctx->global)
Pekka Paalanena5630ea2017-10-12 13:13:42 +0200361 return;
362
363 weston_log("WARNING: debug protocol has been enabled. "
364 "This is a potential denial-of-service attack vector and "
365 "information leak.\n");
366}
367
Marius Vladd9bcc0b2018-12-13 23:03:30 +0200368/** Determine if the debug protocol has been enabled
369 *
370 * \param wc The libweston compositor to verify if debug protocol has been enabled
371 */
372WL_EXPORT bool
373weston_compositor_is_debug_protocol_enabled(struct weston_compositor *wc)
374{
375 return wc->weston_debug->global != NULL;
376}
377
Pekka Paalanena5630ea2017-10-12 13:13:42 +0200378/** Register a new debug stream name, creating a debug scope
379 *
Marius Vlad1e2fda22019-04-07 19:07:16 +0300380 * \param wdc The weston_debug_compositor where to add.
Pekka Paalanena5630ea2017-10-12 13:13:42 +0200381 * \param name The debug stream/scope name; must not be NULL.
382 * \param desc The debug scope description for humans; must not be NULL.
383 * \param begin_cb Optional callback when a client subscribes to this scope.
384 * \param user_data Optional user data pointer for the callback.
385 * \return A valid pointer on success, NULL on failure.
386 *
387 * This function is used to create a debug scope. All debug message printing
388 * happens for a scope, which allows clients to subscribe to the kind of
389 * debug messages they want by \c name.
390 *
391 * \c name must be unique in the \c weston_compositor instance. \c name and
392 * \c description must both be provided. The description is printed when a
393 * client asks for a list of supported debug scopes.
394 *
395 * \c begin_cb, if not NULL, is called when a client subscribes to the
396 * debug scope creating a debug stream. This is for debug scopes that need
397 * to print messages as a response to a client appearing, e.g. printing a
398 * list of windows on demand or a static preamble. The argument \c user_data
399 * is passed in to the callback and is otherwise unused.
400 *
401 * For one-shot debug streams, \c begin_cb should finally call
402 * weston_debug_stream_complete() to close the stream and tell the client
403 * the printing is complete. Otherwise the client expects more to be written
404 * to its file descriptor.
405 *
406 * The debug scope must be destroyed before destroying the
407 * \c weston_compositor.
408 *
409 * \memberof weston_debug_scope
410 * \sa weston_debug_stream, weston_debug_scope_cb
411 */
412WL_EXPORT struct weston_debug_scope *
Marius Vlad1e2fda22019-04-07 19:07:16 +0300413weston_compositor_add_debug_scope(struct weston_debug_compositor *wdc,
Pekka Paalanena5630ea2017-10-12 13:13:42 +0200414 const char *name,
415 const char *description,
416 weston_debug_scope_cb begin_cb,
417 void *user_data)
418{
Pekka Paalanena5630ea2017-10-12 13:13:42 +0200419 struct weston_debug_scope *scope;
420
Marius Vlad1e2fda22019-04-07 19:07:16 +0300421 if (!name || !description) {
Pekka Paalanena5630ea2017-10-12 13:13:42 +0200422 weston_log("Error: cannot add a debug scope without name or description.\n");
423 return NULL;
424 }
425
Pekka Paalanena5630ea2017-10-12 13:13:42 +0200426 if (!wdc) {
427 weston_log("Error: cannot add debug scope '%s', infra not initialized.\n",
428 name);
429 return NULL;
430 }
431
432 if (get_scope(wdc, name)){
433 weston_log("Error: debug scope named '%s' is already registered.\n",
434 name);
435 return NULL;
436 }
437
438 scope = zalloc(sizeof *scope);
439 if (!scope) {
440 weston_log("Error adding debug scope '%s': out of memory.\n",
441 name);
442 return NULL;
443 }
444
445 scope->name = strdup(name);
446 scope->desc = strdup(description);
447 scope->begin_cb = begin_cb;
448 scope->user_data = user_data;
449 wl_list_init(&scope->stream_list);
450
451 if (!scope->name || !scope->desc) {
452 weston_log("Error adding debug scope '%s': out of memory.\n",
453 name);
454 free(scope->name);
455 free(scope->desc);
456 free(scope);
457 return NULL;
458 }
459
460 wl_list_insert(wdc->scope_list.prev, &scope->compositor_link);
461
462 return scope;
463}
464
465/** Destroy a debug scope
466 *
467 * \param scope The debug scope to destroy; may be NULL.
468 *
469 * Destroys the debug scope, closing all open streams subscribed to it and
470 * sending them each a \c weston_debug_stream_v1.failure event.
471 *
472 * \memberof weston_debug_scope
473 */
474WL_EXPORT void
475weston_debug_scope_destroy(struct weston_debug_scope *scope)
476{
477 struct weston_debug_stream *stream;
478
479 if (!scope)
480 return;
481
482 while (!wl_list_empty(&scope->stream_list)) {
483 stream = wl_container_of(scope->stream_list.prev,
484 stream, scope_link);
485
486 stream_close_on_failure(stream, "debug name removed");
487 }
488
489 wl_list_remove(&scope->compositor_link);
490 free(scope->name);
491 free(scope->desc);
492 free(scope);
493}
494
495/** Are there any active subscriptions to the scope?
496 *
497 * \param scope The debug scope to check; may be NULL.
498 * \return True if any streams are open for this scope, false otherwise.
499 *
500 * As printing some debugging messages may be relatively expensive, one
501 * can use this function to determine if there is a need to gather the
502 * debugging information at all. If this function returns false, all
503 * printing for this scope is dropped, so gathering the information is
504 * pointless.
505 *
506 * The return value of this function should not be stored, as new clients
507 * may subscribe to the debug scope later.
508 *
509 * If the given scope is NULL, this function will always return false,
510 * making it safe to use in teardown or destroy code, provided the
511 * scope is initialized to NULL before creation and set to NULL after
512 * destruction.
513 *
514 * \memberof weston_debug_scope
515 */
516WL_EXPORT bool
517weston_debug_scope_is_enabled(struct weston_debug_scope *scope)
518{
519 if (!scope)
520 return false;
521
522 return !wl_list_empty(&scope->stream_list);
523}
524
525/** Write data into a specific debug stream
526 *
527 * \param stream The debug stream to write into; must not be NULL.
528 * \param data[in] Pointer to the data to write.
529 * \param len Number of bytes to write.
530 *
531 * Writes the given data (binary verbatim) into the debug stream.
532 * If \c len is zero or negative, the write is silently dropped.
533 *
534 * Writing is continued until all data has been written or
535 * a write fails. If the write fails due to a signal, it is re-tried.
536 * Otherwise on failure, the stream is closed and
537 * \c weston_debug_stream_v1.failure event is sent to the client.
538 *
539 * \memberof weston_debug_stream
540 */
541WL_EXPORT void
542weston_debug_stream_write(struct weston_debug_stream *stream,
543 const char *data, size_t len)
544{
545 ssize_t len_ = len;
546 ssize_t ret;
547 int e;
548
549 if (stream->fd == -1)
550 return;
551
552 while (len_ > 0) {
553 ret = write(stream->fd, data, len_);
554 e = errno;
555 if (ret < 0) {
556 if (e == EINTR)
557 continue;
558
559 stream_close_on_failure(stream,
560 "Error writing %zd bytes: %s (%d)",
561 len_, strerror(e), e);
562 break;
563 }
564
565 len_ -= ret;
566 data += ret;
567 }
568}
569
570/** Write a formatted string into a specific debug stream (varargs)
571 *
572 * \param stream The debug stream to write into.
573 * \param fmt Printf-style format string.
574 * \param ap Formatting arguments.
575 *
576 * The behavioral details are the same as for weston_debug_stream_write().
577 *
578 * \memberof weston_debug_stream
579 */
580WL_EXPORT void
581weston_debug_stream_vprintf(struct weston_debug_stream *stream,
582 const char *fmt, va_list ap)
583{
584 char *str;
585 int len;
586
587 len = vasprintf(&str, fmt, ap);
588 if (len >= 0) {
589 weston_debug_stream_write(stream, str, len);
590 free(str);
591 } else {
592 stream_close_on_failure(stream, "Out of memory");
593 }
594}
595
596/** Write a formatted string into a specific debug stream
597 *
598 * \param stream The debug stream to write into.
599 * \param fmt Printf-style format string and arguments.
600 *
601 * The behavioral details are the same as for weston_debug_stream_write().
602 *
603 * \memberof weston_debug_stream
604 */
605WL_EXPORT void
606weston_debug_stream_printf(struct weston_debug_stream *stream,
607 const char *fmt, ...)
608{
609 va_list ap;
610
611 va_start(ap, fmt);
612 weston_debug_stream_vprintf(stream, fmt, ap);
613 va_end(ap);
614}
615
616/** Close the debug stream and send success event
617 *
618 * \param stream The debug stream to close.
619 *
620 * Closes the debug stream and sends \c weston_debug_stream_v1.complete
621 * event to the client. This tells the client the debug information dump
622 * is complete.
623 *
624 * \memberof weston_debug_stream
625 */
626WL_EXPORT void
627weston_debug_stream_complete(struct weston_debug_stream *stream)
628{
629 stream_close_unlink(stream);
630 weston_debug_stream_v1_send_complete(stream->resource);
631}
632
633/** Write debug data for a scope
634 *
635 * \param scope The debug scope to write for; may be NULL, in which case
636 * nothing will be written.
637 * \param data[in] Pointer to the data to write.
638 * \param len Number of bytes to write.
639 *
640 * Writes the given data to all subscribed clients' streams.
641 *
642 * The behavioral details for each stream are the same as for
643 * weston_debug_stream_write().
644 *
645 * \memberof weston_debug_scope
646 */
647WL_EXPORT void
648weston_debug_scope_write(struct weston_debug_scope *scope,
649 const char *data, size_t len)
650{
651 struct weston_debug_stream *stream;
652
653 if (!scope)
654 return;
655
656 wl_list_for_each(stream, &scope->stream_list, scope_link)
657 weston_debug_stream_write(stream, data, len);
658}
659
660/** Write a formatted string for a scope (varargs)
661 *
662 * \param scope The debug scope to write for; may be NULL, in which case
663 * nothing will be written.
664 * \param fmt Printf-style format string.
665 * \param ap Formatting arguments.
666 *
667 * Writes to formatted string to all subscribed clients' streams.
668 *
669 * The behavioral details for each stream are the same as for
670 * weston_debug_stream_write().
671 *
672 * \memberof weston_debug_scope
673 */
674WL_EXPORT void
675weston_debug_scope_vprintf(struct weston_debug_scope *scope,
676 const char *fmt, va_list ap)
677{
678 static const char oom[] = "Out of memory";
679 char *str;
680 int len;
681
682 if (!weston_debug_scope_is_enabled(scope))
683 return;
684
685 len = vasprintf(&str, fmt, ap);
686 if (len >= 0) {
687 weston_debug_scope_write(scope, str, len);
688 free(str);
689 } else {
690 weston_debug_scope_write(scope, oom, sizeof oom - 1);
691 }
692}
693
694/** Write a formatted string for a scope
695 *
696 * \param scope The debug scope to write for; may be NULL, in which case
697 * nothing will be written.
698 * \param fmt Printf-style format string and arguments.
699 *
700 * Writes to formatted string to all subscribed clients' streams.
701 *
702 * The behavioral details for each stream are the same as for
703 * weston_debug_stream_write().
704 *
705 * \memberof weston_debug_scope
706 */
707WL_EXPORT void
708weston_debug_scope_printf(struct weston_debug_scope *scope,
709 const char *fmt, ...)
710{
711 va_list ap;
712
713 va_start(ap, fmt);
714 weston_debug_scope_vprintf(scope, fmt, ap);
715 va_end(ap);
716}
717
718/** Write debug scope name and current time into string
719 *
720 * \param scope[in] debug scope; may be NULL
721 * \param buf[out] Buffer to store the string.
722 * \param len Available size in the buffer in bytes.
723 * \return \c buf
724 *
725 * Reads the current local wall-clock time and formats it into a string.
726 * and append the debug scope name to it, if a scope is available.
727 * The string is NUL-terminated, even if truncated.
728 */
729WL_EXPORT char *
730weston_debug_scope_timestamp(struct weston_debug_scope *scope,
731 char *buf, size_t len)
732{
733 struct timeval tv;
734 struct tm *bdt;
735 char string[128];
736 size_t ret = 0;
737
738 gettimeofday(&tv, NULL);
739
740 bdt = localtime(&tv.tv_sec);
741 if (bdt)
742 ret = strftime(string, sizeof string,
743 "%Y-%m-%d %H:%M:%S", bdt);
744
745 if (ret > 0) {
746 snprintf(buf, len, "[%s.%03ld][%s]", string,
747 tv.tv_usec / 1000,
748 (scope) ? scope->name : "no scope");
749 } else {
750 snprintf(buf, len, "[?][%s]",
751 (scope) ? scope->name : "no scope");
752 }
753
754 return buf;
755}