Coverage Report

Created: 2026-03-31 07:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ruby/debug.c
Line
Count
Source
1
/**********************************************************************
2
3
  debug.c -
4
5
  $Author$
6
  created at: 04/08/25 02:31:54 JST
7
8
  Copyright (C) 2004-2007 Koichi Sasada
9
10
**********************************************************************/
11
12
#include "ruby/internal/config.h"
13
14
#include <stdio.h>
15
16
#include "eval_intern.h"
17
#include "encindex.h"
18
#include "id.h"
19
#include "internal/signal.h"
20
#include "ruby/encoding.h"
21
#include "ruby/io.h"
22
#include "ruby/ruby.h"
23
#include "ruby/util.h"
24
#include "symbol.h"
25
#include "vm_core.h"
26
#include "vm_debug.h"
27
#include "vm_callinfo.h"
28
#include "ruby/thread_native.h"
29
#include "ractor_core.h"
30
31
/* This is the only place struct RIMemo is actually used */
32
struct RIMemo {
33
    VALUE flags;
34
    VALUE v0;
35
    VALUE v1;
36
    VALUE v2;
37
    VALUE v3;
38
};
39
40
/* for gdb */
41
const union {
42
    enum ruby_special_consts    special_consts;
43
    enum ruby_value_type        value_type;
44
    enum ruby_tag_type          tag_type;
45
    enum node_type              node_type;
46
    enum ruby_method_ids        method_ids;
47
    enum ruby_id_types          id_types;
48
    enum ruby_fl_type           fl_types;
49
    enum ruby_fl_ushift         fl_ushift;
50
    enum ruby_encoding_consts   encoding_consts;
51
    enum ruby_coderange_type    enc_coderange_types;
52
    enum ruby_econv_flag_type   econv_flag_types;
53
    rb_econv_result_t           econv_result;
54
    enum ruby_preserved_encindex encoding_index;
55
    enum ruby_robject_flags     robject_flags;
56
    enum ruby_rmodule_flags     rmodule_flags;
57
    enum ruby_rstring_flags     rstring_flags;
58
    enum ruby_rarray_flags      rarray_flags;
59
    enum ruby_rarray_consts     rarray_consts;
60
    enum rbimpl_typeddata_flags rtypeddata_consts;
61
    enum {
62
        RUBY_FMODE_READABLE   = FMODE_READABLE,
63
        RUBY_FMODE_WRITABLE   = FMODE_WRITABLE,
64
        RUBY_FMODE_READWRITE    = FMODE_READWRITE,
65
        RUBY_FMODE_BINMODE    = FMODE_BINMODE,
66
        RUBY_FMODE_SYNC     = FMODE_SYNC,
67
        RUBY_FMODE_TTY      = FMODE_TTY,
68
        RUBY_FMODE_DUPLEX   = FMODE_DUPLEX,
69
        RUBY_FMODE_APPEND   = FMODE_APPEND,
70
        RUBY_FMODE_CREATE   = FMODE_CREATE,
71
        RUBY_FMODE_NOREVLOOKUP    = 0x00000100,
72
        RUBY_FMODE_TRUNC    = FMODE_TRUNC,
73
        RUBY_FMODE_TEXTMODE   = FMODE_TEXTMODE,
74
        RUBY_FMODE_EXTERNAL   = 0x00010000,
75
        RUBY_FMODE_SETENC_BY_BOM  = FMODE_SETENC_BY_BOM,
76
        RUBY_FMODE_UNIX     = 0x00200000,
77
        RUBY_FMODE_INET     = 0x00400000,
78
        RUBY_FMODE_INET6    = 0x00800000,
79
80
        RUBY_NODE_TYPESHIFT = NODE_TYPESHIFT,
81
        RUBY_NODE_TYPEMASK  = NODE_TYPEMASK,
82
        RUBY_NODE_LSHIFT    = NODE_LSHIFT,
83
        RUBY_NODE_FL_NEWLINE   = NODE_FL_NEWLINE
84
    } various;
85
    union {
86
        enum imemo_type                     types;
87
        enum {RUBY_IMEMO_MASK = IMEMO_MASK} mask;
88
        struct RIMemo                      *ptr;
89
    } imemo;
90
    struct RSymbol *symbol_ptr;
91
    enum vm_call_flag_bits vm_call_flags;
92
} ruby_dummy_gdb_enums;
93
94
const SIGNED_VALUE RUBY_NODE_LMASK = NODE_LMASK;
95
96
int
97
ruby_debug_print_indent(int level, int debug_level, int indent_level)
98
0
{
99
0
    if (level < debug_level) {
100
0
        fprintf(stderr, "%*s", indent_level, "");
101
0
        fflush(stderr);
102
0
        return TRUE;
103
0
    }
104
0
    return FALSE;
105
0
}
106
107
void
108
ruby_debug_printf(const char *format, ...)
109
0
{
110
0
    va_list ap;
111
0
    va_start(ap, format);
112
0
    vfprintf(stderr, format, ap);
113
0
    va_end(ap);
114
0
}
115
116
#include "internal/gc.h"
117
118
VALUE
119
ruby_debug_print_value(int level, int debug_level, const char *header, VALUE obj)
120
0
{
121
0
    if (level < debug_level) {
122
0
        char buff[0x100];
123
0
        rb_raw_obj_info(buff, 0x100, obj);
124
125
0
        fprintf(stderr, "DBG> %s: %s\n", header, buff);
126
0
        fflush(stderr);
127
0
    }
128
0
    return obj;
129
0
}
130
131
void
132
ruby_debug_print_v(VALUE v)
133
0
{
134
0
    ruby_debug_print_value(0, 1, "", v);
135
0
}
136
137
ID
138
ruby_debug_print_id(int level, int debug_level, const char *header, ID id)
139
0
{
140
0
    if (level < debug_level) {
141
0
        fprintf(stderr, "DBG> %s: %s\n", header, rb_id2name(id));
142
0
        fflush(stderr);
143
0
    }
144
0
    return id;
145
0
}
146
147
NODE *
148
ruby_debug_print_node(int level, int debug_level, const char *header, const NODE *node)
149
0
{
150
0
    if (level < debug_level) {
151
0
        fprintf(stderr, "DBG> %s: %s (id: %d, line: %d, location: (%d,%d)-(%d,%d))\n",
152
0
                header, ruby_node_name(nd_type(node)), nd_node_id(node), nd_line(node),
153
0
                nd_first_lineno(node), nd_first_column(node),
154
0
                nd_last_lineno(node), nd_last_column(node));
155
0
    }
156
0
    return (NODE *)node;
157
0
}
158
159
void
160
ruby_debug_print_n(const NODE *node)
161
0
{
162
0
    ruby_debug_print_node(0, 1, "", node);
163
0
}
164
165
void
166
ruby_debug_breakpoint(void)
167
0
{
168
    /* */
169
0
}
170
171
#if defined _WIN32
172
extern int ruby_w32_rtc_error;
173
#endif
174
#if defined _WIN32 || defined __CYGWIN__
175
#include <windows.h>
176
UINT ruby_w32_codepage[2];
177
#endif
178
extern int ruby_rgengc_debug;
179
extern int ruby_on_ci;
180
181
int
182
ruby_env_debug_option(const char *str, int len, void *arg)
183
0
{
184
0
    int ov;
185
0
    size_t retlen;
186
0
    unsigned long n;
187
0
#define NAME_MATCH(name) (len == sizeof(name) - 1 && strncmp(str, (name), len) == 0)
188
0
#define SET_WHEN(name, var, val) do {     \
189
0
        if (NAME_MATCH(name)) { \
190
0
            (var) = (val);        \
191
0
            return 1;         \
192
0
        }            \
193
0
    } while (0)
194
0
#define NAME_MATCH_VALUE(name)        \
195
0
    ((size_t)len >= sizeof(name)-1 &&     \
196
0
     strncmp(str, (name), sizeof(name)-1) == 0 && \
197
0
     ((len == sizeof(name)-1 && !(len = 0)) ||    \
198
0
      (str[sizeof(name)-1] == '=' &&     \
199
0
       (str += sizeof(name), len -= sizeof(name), 1))))
200
0
#define SET_UINT(val) do { \
201
0
        n = ruby_scan_digits(str, len, 10, &retlen, &ov); \
202
0
        if (!ov && retlen) { \
203
0
            val = (unsigned int)n; \
204
0
        } \
205
0
        str += retlen; \
206
0
        len -= retlen; \
207
0
    } while (0)
208
0
#define SET_UINT_LIST(name, vals, num) do { \
209
0
        int i; \
210
0
        for (i = 0; i < (num); ++i) { \
211
0
            SET_UINT((vals)[i]); \
212
0
            if (!len || *str != ':') break; \
213
0
            ++str; \
214
0
            --len; \
215
0
        } \
216
0
        if (len > 0) { \
217
0
            fprintf(stderr, "ignored "name" option: '%.*s'\n", len, str); \
218
0
        } \
219
0
    } while (0)
220
0
#define SET_WHEN_UINT(name, vals, num, req) \
221
0
    if (NAME_MATCH_VALUE(name)) { \
222
0
        if (!len) req; \
223
0
        else SET_UINT_LIST(name, vals, num); \
224
0
        return 1; \
225
0
    }
226
227
0
    if (NAME_MATCH("gc_stress")) {
228
0
        rb_gc_initial_stress_set(Qtrue);
229
0
        return 1;
230
0
    }
231
0
    SET_WHEN("core", ruby_enable_coredump, 1);
232
0
    SET_WHEN("ci", ruby_on_ci, 1);
233
0
    SET_WHEN_UINT("rgengc", &ruby_rgengc_debug, 1, ruby_rgengc_debug = 1);
234
#if defined _WIN32
235
    SET_WHEN("rtc_error", ruby_w32_rtc_error, 1);
236
#endif
237
#if defined _WIN32 || defined __CYGWIN__
238
    SET_WHEN_UINT("codepage", ruby_w32_codepage, numberof(ruby_w32_codepage),
239
                  fprintf(stderr, "missing codepage argument"));
240
#endif
241
0
    return 0;
242
0
}
243
244
static void
245
set_debug_option(const char *str, int len, void *arg)
246
0
{
247
0
    if (!ruby_env_debug_option(str, len, arg)) {
248
0
        fprintf(stderr, "unexpected debug option: %.*s\n", len, str);
249
0
    }
250
0
}
251
252
#if USE_RUBY_DEBUG_LOG
253
static void setup_debug_log(void);
254
#else
255
#define setup_debug_log()
256
#endif
257
258
void
259
ruby_set_debug_option(const char *str)
260
0
{
261
0
    ruby_each_words(str, set_debug_option, 0);
262
0
    setup_debug_log();
263
0
}
264
265
#if USE_RUBY_DEBUG_LOG
266
267
// RUBY_DEBUG_LOG features
268
// See vm_debug.h comments for details.
269
270
#define MAX_DEBUG_LOG             0x1000
271
#define MAX_DEBUG_LOG_MESSAGE_LEN 0x0200
272
#define MAX_DEBUG_LOG_FILTER_LEN  0x0020
273
#define MAX_DEBUG_LOG_FILTER_NUM  0x0010
274
275
enum ruby_debug_log_mode ruby_debug_log_mode;
276
277
struct debug_log_filter {
278
    enum debug_log_filter_type {
279
        dlf_all,
280
        dlf_file, // "file:..."
281
        dlf_func, // "func:..."
282
    } type;
283
    bool negative;
284
    char str[MAX_DEBUG_LOG_FILTER_LEN];
285
};
286
287
static const char *dlf_type_names[] = {
288
    "all",
289
    "file",
290
    "func",
291
};
292
293
#ifdef MAX_PATH
294
#define DEBUG_LOG_MAX_PATH (MAX_PATH-1)
295
#else
296
#define DEBUG_LOG_MAX_PATH 255
297
#endif
298
299
static struct {
300
    char *mem;
301
    unsigned int cnt;
302
    struct debug_log_filter filters[MAX_DEBUG_LOG_FILTER_NUM];
303
    unsigned int filters_num;
304
    bool show_pid;
305
    rb_nativethread_lock_t lock;
306
    char output_file[DEBUG_LOG_MAX_PATH+1];
307
    FILE *output;
308
} debug_log;
309
310
static char *
311
RUBY_DEBUG_LOG_MEM_ENTRY(unsigned int index)
312
{
313
    return &debug_log.mem[MAX_DEBUG_LOG_MESSAGE_LEN * index];
314
}
315
316
static enum debug_log_filter_type
317
filter_type(const char *str, int *skiplen)
318
{
319
    if (strncmp(str, "file:", 5) == 0) {
320
        *skiplen = 5;
321
        return dlf_file;
322
    }
323
    else if(strncmp(str, "func:", 5) == 0) {
324
        *skiplen = 5;
325
        return dlf_func;
326
    }
327
    else {
328
        *skiplen = 0;
329
        return dlf_all;
330
    }
331
}
332
333
static void
334
setup_debug_log_filter(void)
335
{
336
    const char *filter_config = getenv("RUBY_DEBUG_LOG_FILTER");
337
338
    if (filter_config && strlen(filter_config) > 0) {
339
        unsigned int i;
340
        for (i=0; i<MAX_DEBUG_LOG_FILTER_NUM && filter_config; i++) {
341
            size_t len;
342
            const char *str = filter_config;
343
            const char *p;
344
345
            if ((p = strchr(str, ',')) == NULL) {
346
                len = strlen(str);
347
                filter_config = NULL;
348
            }
349
            else {
350
                len = p - str - 1; // 1 is ','
351
                filter_config = p + 1;
352
            }
353
354
            // positive/negative
355
            if (*str == '-') {
356
                debug_log.filters[i].negative = true;
357
                str++;
358
            }
359
            else if (*str == '+') {
360
                // negative is false on default.
361
                str++;
362
            }
363
364
            // type
365
            int skiplen;
366
            debug_log.filters[i].type = filter_type(str, &skiplen);
367
            len -= skiplen;
368
369
            if (len >= MAX_DEBUG_LOG_FILTER_LEN) {
370
                fprintf(stderr, "too long: %s (max:%d)\n", str, MAX_DEBUG_LOG_FILTER_LEN - 1);
371
                exit(EXIT_FAILURE);
372
            }
373
374
            // body
375
            strncpy(debug_log.filters[i].str, str + skiplen, len);
376
            debug_log.filters[i].str[len] = 0;
377
        }
378
        debug_log.filters_num = i;
379
380
        for (i=0; i<debug_log.filters_num; i++) {
381
            fprintf(stderr, "RUBY_DEBUG_LOG_FILTER[%d]=%s (%s%s)\n", i,
382
                    debug_log.filters[i].str,
383
                    debug_log.filters[i].negative ? "-" : "",
384
                    dlf_type_names[debug_log.filters[i].type]);
385
        }
386
    }
387
}
388
389
static void
390
setup_debug_log(void)
391
{
392
    // check RUBY_DEBUG_LOG
393
    const char *log_config = getenv("RUBY_DEBUG_LOG");
394
    if (log_config && strlen(log_config) > 0) {
395
        if (strcmp(log_config, "mem") == 0) {
396
            debug_log.mem = (char *)malloc(MAX_DEBUG_LOG * MAX_DEBUG_LOG_MESSAGE_LEN);
397
            if (debug_log.mem == NULL) {
398
                fprintf(stderr, "setup_debug_log failed (can't allocate memory)\n");
399
                exit(EXIT_FAILURE);
400
            }
401
            ruby_debug_log_mode |= ruby_debug_log_memory;
402
        }
403
        else if (strcmp(log_config, "stderr") == 0) {
404
            ruby_debug_log_mode |= ruby_debug_log_stderr;
405
        }
406
        else {
407
            ruby_debug_log_mode |= ruby_debug_log_file;
408
409
            // pid extension with %p
410
            unsigned long len = strlen(log_config);
411
412
            for (unsigned long i=0, j=0; i<len; i++) {
413
                const char c = log_config[i];
414
415
                if (c == '%') {
416
                    i++;
417
                    switch (log_config[i]) {
418
                      case '%':
419
                        debug_log.output_file[j++] = '%';
420
                        break;
421
                      case 'p':
422
                        snprintf(debug_log.output_file + j, DEBUG_LOG_MAX_PATH - j, "%d", getpid());
423
                        j = strlen(debug_log.output_file);
424
                        break;
425
                      default:
426
                        fprintf(stderr, "can not parse RUBY_DEBUG_LOG filename: %s\n", log_config);
427
                        exit(EXIT_FAILURE);
428
                    }
429
                }
430
                else {
431
                    debug_log.output_file[j++] = c;
432
                }
433
434
                if (j >= DEBUG_LOG_MAX_PATH) {
435
                    fprintf(stderr, "RUBY_DEBUG_LOG=%s is too long\n", log_config);
436
                    exit(EXIT_FAILURE);
437
                }
438
            }
439
440
            if ((debug_log.output = fopen(debug_log.output_file, "w")) == NULL) {
441
                fprintf(stderr, "can not open %s for RUBY_DEBUG_LOG\n", log_config);
442
                exit(EXIT_FAILURE);
443
            }
444
            setvbuf(debug_log.output, NULL, _IONBF, 0);
445
        }
446
447
        fprintf(stderr, "RUBY_DEBUG_LOG=%s %s%s%s\n", log_config,
448
                (ruby_debug_log_mode & ruby_debug_log_memory) ? "[mem]" : "",
449
                (ruby_debug_log_mode & ruby_debug_log_stderr) ? "[stderr]" : "",
450
                (ruby_debug_log_mode & ruby_debug_log_file)   ? "[file]" : "");
451
        if (debug_log.output_file[0]) {
452
            fprintf(stderr, "RUBY_DEBUG_LOG filename=%s\n", debug_log.output_file);
453
        }
454
455
        rb_nativethread_lock_initialize(&debug_log.lock);
456
457
        setup_debug_log_filter();
458
459
        if (getenv("RUBY_DEBUG_LOG_PID")) {
460
            debug_log.show_pid = true;
461
        }
462
    }
463
}
464
465
static bool
466
check_filter(const char *str, const struct debug_log_filter *filter, bool *state)
467
{
468
    if (filter->negative) {
469
        if (strstr(str, filter->str) == NULL) {
470
            *state = true;
471
            return false;
472
        }
473
        else {
474
            *state = false;
475
            return true;
476
        }
477
    }
478
    else {
479
        if (strstr(str, filter->str) != NULL) {
480
            *state = true;
481
            return true;
482
        }
483
        else {
484
            *state = false;
485
            return false;
486
        }
487
    }
488
}
489
490
//
491
// RUBY_DEBUG_LOG_FILTER=-foo,-bar,baz,boo
492
// returns true if
493
//   (func_name or file_name) doesn't contain foo
494
// and
495
//   (func_name or file_name) doesn't contain bar
496
// and
497
//   (func_name or file_name) contains baz or boo
498
//
499
// RUBY_DEBUG_LOG_FILTER=foo,bar,-baz,-boo
500
// returns true if
501
//   (func_name or file_name) contains foo or bar
502
// or
503
//   (func_name or file_name) doesn't contain baz and
504
//   (func_name or file_name) doesn't contain boo and
505
//
506
// You can specify "file:" (ex file:foo) or "func:" (ex  func:foo)
507
// prefixes to specify the filter for.
508
//
509
bool
510
ruby_debug_log_filter(const char *func_name, const char *file_name)
511
{
512
    if (debug_log.filters_num > 0) {
513
        bool state = false;
514
515
        for (unsigned int i = 0; i<debug_log.filters_num; i++) {
516
            const struct debug_log_filter *filter = &debug_log.filters[i];
517
518
            switch (filter->type) {
519
              case dlf_all:
520
                if (check_filter(func_name, filter, &state)) return state;
521
                if (check_filter(file_name, filter, &state)) return state;
522
                break;
523
              case dlf_func:
524
                if (check_filter(func_name, filter, &state)) return state;
525
                break;
526
              case dlf_file:
527
                if (check_filter(file_name, filter, &state)) return state;
528
                break;
529
            }
530
        }
531
        return state;
532
    }
533
    else {
534
        return true;
535
    }
536
}
537
538
static const char *
539
pretty_filename(const char *path)
540
{
541
    // basename is one idea.
542
    const char *s;
543
    while ((s = strchr(path, '/')) != NULL) {
544
        path = s+1;
545
    }
546
    return path;
547
}
548
549
#undef ruby_debug_log
550
void
551
ruby_debug_log(const char *file, int line, const char *func_name, const char *fmt, ...)
552
{
553
    char buff[MAX_DEBUG_LOG_MESSAGE_LEN] = {0};
554
    int len = 0;
555
    int r = 0;
556
557
    if (debug_log.show_pid) {
558
        r = snprintf(buff + len, MAX_DEBUG_LOG_MESSAGE_LEN, "pid:%d\t", getpid());
559
        if (r < 0) rb_bug("ruby_debug_log returns %d", r);
560
        len += r;
561
    }
562
563
    // message title
564
    if (func_name && len < MAX_DEBUG_LOG_MESSAGE_LEN) {
565
        r = snprintf(buff + len, MAX_DEBUG_LOG_MESSAGE_LEN, "%s\t", func_name);
566
        if (r < 0) rb_bug("ruby_debug_log returns %d", r);
567
        len += r;
568
    }
569
570
    // message
571
    if (fmt && len < MAX_DEBUG_LOG_MESSAGE_LEN) {
572
        va_list args;
573
        va_start(args, fmt);
574
        r = vsnprintf(buff + len, MAX_DEBUG_LOG_MESSAGE_LEN - len, fmt, args);
575
        va_end(args);
576
        if (r < 0) rb_bug("ruby_debug_log vsnprintf() returns %d", r);
577
        len += r;
578
    }
579
580
    // optional information
581
582
    // C location
583
    if (file && len < MAX_DEBUG_LOG_MESSAGE_LEN) {
584
        r = snprintf(buff + len, MAX_DEBUG_LOG_MESSAGE_LEN, "\t%s:%d", pretty_filename(file), line);
585
        if (r < 0) rb_bug("ruby_debug_log returns %d", r);
586
        len += r;
587
    }
588
589
    rb_execution_context_t *ec = rb_current_execution_context(false);
590
591
    // Ruby location
592
    int ruby_line;
593
    const char *ruby_file = ec ? rb_source_location_cstr(&ruby_line) : NULL;
594
595
    if (len < MAX_DEBUG_LOG_MESSAGE_LEN) {
596
        if (ruby_file) {
597
            r = snprintf(buff + len, MAX_DEBUG_LOG_MESSAGE_LEN - len, "\t%s:%d", pretty_filename(ruby_file), ruby_line);
598
        }
599
        else {
600
            r = snprintf(buff + len, MAX_DEBUG_LOG_MESSAGE_LEN - len, "\t");
601
        }
602
        if (r < 0) rb_bug("ruby_debug_log returns %d", r);
603
        len += r;
604
    }
605
606
#ifdef RUBY_NT_SERIAL
607
    // native thread information
608
    if (len < MAX_DEBUG_LOG_MESSAGE_LEN) {
609
        r = snprintf(buff + len, MAX_DEBUG_LOG_MESSAGE_LEN - len, "\tnt:%d", ruby_nt_serial);
610
        if (r < 0) rb_bug("ruby_debug_log returns %d", r);
611
        len += r;
612
    }
613
#endif
614
615
    if (ec) {
616
        rb_thread_t *th = ec ? rb_ec_thread_ptr(ec) : NULL;
617
618
        // ractor information
619
        if (ruby_single_main_ractor == NULL) {
620
            rb_ractor_t *cr = th ? th->ractor : NULL;
621
            rb_vm_t *vm = GET_VM();
622
623
            if (r && len < MAX_DEBUG_LOG_MESSAGE_LEN) {
624
                r = snprintf(buff + len, MAX_DEBUG_LOG_MESSAGE_LEN - len, "\tr:#%d/%u (%u)",
625
                             cr ? (int)rb_ractor_id(cr) : -1, vm->ractor.cnt, vm->ractor.sched.running_cnt);
626
627
                if (r < 0) rb_bug("ruby_debug_log returns %d", r);
628
                len += r;
629
            }
630
        }
631
632
        // thread information
633
        if (th && r && len < MAX_DEBUG_LOG_MESSAGE_LEN) {
634
            rb_execution_context_t *rec = th->ractor ? th->ractor->threads.running_ec : NULL;
635
            const rb_thread_t *rth = rec ? rec->thread_ptr : NULL;
636
            const rb_thread_t *sth = th->ractor ? th->ractor->threads.sched.running : NULL;
637
638
            if (rth != th || sth != th) {
639
                r = snprintf(buff + len, MAX_DEBUG_LOG_MESSAGE_LEN - len, "\tth:%u (rth:%d,sth:%d)",
640
                             rb_th_serial(th), rth ? (int)rb_th_serial(rth) : -1, sth ? (int)rb_th_serial(sth) : -1);
641
            }
642
            else {
643
                r = snprintf(buff + len, MAX_DEBUG_LOG_MESSAGE_LEN - len, "\tth:%u", rb_th_serial(th));
644
            }
645
            if (r < 0) rb_bug("ruby_debug_log returns %d", r);
646
            len += r;
647
        }
648
    }
649
650
    rb_nativethread_lock_lock(&debug_log.lock);
651
    {
652
        unsigned int cnt = debug_log.cnt++;
653
654
        if (ruby_debug_log_mode & ruby_debug_log_memory) {
655
            unsigned int index = cnt % MAX_DEBUG_LOG;
656
            char *dst = RUBY_DEBUG_LOG_MEM_ENTRY(index);
657
            strncpy(dst, buff, MAX_DEBUG_LOG_MESSAGE_LEN);
658
        }
659
        if (ruby_debug_log_mode & ruby_debug_log_stderr) {
660
            fprintf(stderr, "%4u: %s\n", cnt, buff);
661
        }
662
        if (ruby_debug_log_mode & ruby_debug_log_file) {
663
            fprintf(debug_log.output, "%u\t%s\n", cnt, buff);
664
        }
665
    }
666
    rb_nativethread_lock_unlock(&debug_log.lock);
667
}
668
669
// for debugger
670
static void
671
debug_log_dump(FILE *out, unsigned int n)
672
{
673
    if (ruby_debug_log_mode & ruby_debug_log_memory) {
674
        unsigned int size = debug_log.cnt > MAX_DEBUG_LOG ? MAX_DEBUG_LOG : debug_log.cnt;
675
        unsigned int current_index = debug_log.cnt % MAX_DEBUG_LOG;
676
        if (n == 0) n = size;
677
        if (n > size) n = size;
678
679
        for (unsigned int i=0; i<n; i++) {
680
            int index = current_index - size + i;
681
            if (index < 0) index += MAX_DEBUG_LOG;
682
            VM_ASSERT(index <= MAX_DEBUG_LOG);
683
            const char *mesg = RUBY_DEBUG_LOG_MEM_ENTRY(index);
684
            fprintf(out, "%4u: %s\n", debug_log.cnt - size + i, mesg);
685
        }
686
    }
687
    else {
688
        fprintf(stderr, "RUBY_DEBUG_LOG=mem is not specified.");
689
    }
690
}
691
692
// for debuggers
693
694
void
695
ruby_debug_log_print(unsigned int n)
696
{
697
    debug_log_dump(stderr, n);
698
}
699
700
void
701
ruby_debug_log_dump(const char *fname, unsigned int n)
702
{
703
    FILE *fp = fopen(fname, "w");
704
    if (fp == NULL) {
705
        fprintf(stderr, "can't open %s. give up.\n", fname);
706
    }
707
    else {
708
        debug_log_dump(fp, n);
709
        fclose(fp);
710
    }
711
}
712
713
#else
714
715
#undef ruby_debug_log
716
void
717
ruby_debug_log(const char *file, int line, const char *func_name, const char *fmt, ...)
718
0
{
719
0
    va_list args;
720
721
0
    fprintf(stderr, "[%s:%d] %s: ", file, line, func_name);
722
723
0
    va_start(args, fmt);
724
0
    vfprintf(stderr, fmt, args);
725
0
    va_end(args);
726
727
    fprintf(stderr, "\n");
728
0
}
729
730
#endif // #if USE_RUBY_DEBUG_LOG