Coverage Report

Created: 2026-10-28 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/r-source/src/main/debug.c
Line
Count
Source
1
/*
2
 *  R : A Computer Language for Statistical Data Analysis
3
 *  Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
4
 *  Copyright (C) 1998-2025   The R Core Team.
5
 *
6
 *  This program is free software; you can redistribute it and/or modify
7
 *  it under the terms of the GNU General Public License as published by
8
 *  the Free Software Foundation; either version 2 of the License, or
9
 *  (at your option) any later version.
10
 *
11
 *  This program is distributed in the hope that it will be useful,
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 *  GNU General Public License for more details.
15
 *
16
 *  You should have received a copy of the GNU General Public License
17
 *  along with this program; if not, a copy is available at
18
 *  https://www.R-project.org/Licenses/
19
 */
20
21
#ifdef HAVE_CONFIG_H
22
#include <config.h>
23
#endif
24
25
#define R_USE_SIGNALS 1
26
#include <Defn.h>
27
#include <Internal.h>
28
29
attribute_hidden SEXP do_debug(SEXP call, SEXP op, SEXP args, SEXP rho)
30
0
{
31
0
    SEXP ans = R_NilValue;
32
33
0
    checkArity(op,args);
34
0
#define find_char_fun \
35
0
    if (isValidString(CAR(args))) {       \
36
0
  SEXP s;             \
37
0
  PROTECT(s = installTrChar(STRING_ELT(CAR(args), 0)));  \
38
0
  SETCAR(args, findFun(s, rho));        \
39
0
  UNPROTECT(1);            \
40
0
    }
41
0
    find_char_fun
42
43
0
    if (TYPEOF(CAR(args)) != CLOSXP &&
44
0
  TYPEOF(CAR(args)) != SPECIALSXP &&
45
0
  TYPEOF(CAR(args)) != BUILTINSXP)
46
0
  error(_("argument must be a function"));
47
0
    switch(PRIMVAL(op)) {
48
0
    case 0: // debug()
49
0
  SET_RDEBUG(CAR(args), 1);
50
0
  break;
51
0
    case 1: // undebug()
52
0
  if( RDEBUG(CAR(args)) != 1 )
53
0
      warning("argument is not being debugged");
54
0
  SET_RDEBUG(CAR(args), 0);
55
0
  break;
56
0
    case 2: // isdebugged()
57
0
  ans = ScalarLogical(RDEBUG(CAR(args)));
58
0
  break;
59
0
    case 3: // debugonce()
60
0
  SET_RSTEP(CAR(args), 1);
61
0
  break;
62
0
    }
63
0
    return ans;
64
0
}
65
66
/* primitives .primTrace() and .primUntrace() */
67
attribute_hidden SEXP do_trace(SEXP call, SEXP op, SEXP args, SEXP rho)
68
0
{
69
0
    checkArity(op, args);
70
71
0
    find_char_fun
72
73
0
    if (TYPEOF(CAR(args)) != CLOSXP &&
74
0
  TYPEOF(CAR(args)) != SPECIALSXP &&
75
0
  TYPEOF(CAR(args)) != BUILTINSXP)
76
0
      errorcall(call, _("argument must be a function"));
77
78
0
    switch(PRIMVAL(op)) {
79
0
    case 0:
80
0
  SET_RTRACE(CAR(args), 1);
81
0
  break;
82
0
    case 1:
83
0
  SET_RTRACE(CAR(args), 0);
84
0
  break;
85
0
    }
86
0
    return R_NilValue;
87
0
}
88
89
90
/* maintain global trace & debug state */
91
92
static Rboolean tracing_state = TRUE, debugging_state = TRUE;
93
0
#define GET_TRACE_STATE tracing_state
94
0
#define GET_DEBUG_STATE debugging_state
95
0
#define SET_TRACE_STATE(value) tracing_state = value
96
0
#define SET_DEBUG_STATE(value) debugging_state = value
97
98
attribute_hidden SEXP do_traceOnOff(SEXP call, SEXP op, SEXP args, SEXP rho)
99
0
{
100
0
    checkArity(op, args);
101
0
    SEXP onOff = CAR(args);
102
0
    Rboolean trace = (PRIMVAL(op) == 0),
103
0
  prev = trace ? GET_TRACE_STATE : GET_DEBUG_STATE;
104
105
0
    if(length(onOff) > 0) {
106
0
  int _new = asLogical(onOff);
107
0
  if(_new == TRUE || _new == FALSE)
108
0
      if(trace) SET_TRACE_STATE((Rboolean) _new);
109
0
      else      SET_DEBUG_STATE((Rboolean) _new);
110
0
  else
111
0
      error(_("Value for '%s' must be TRUE or FALSE"),
112
0
      trace ? "tracingState" : "debuggingState");
113
0
    }
114
0
    return ScalarLogical(prev);
115
0
}
116
117
// GUIs, packages, etc can query:
118
attribute_hidden /* would need to be in an installed header if not hidden */
119
0
Rboolean R_current_debug_state(void) { return GET_DEBUG_STATE; }
120
attribute_hidden /* would need to be in an installed header if not hidden */
121
0
Rboolean R_current_trace_state(void) { return GET_TRACE_STATE; }
122
123
124
/* memory tracing */
125
/* report when a traced object is duplicated */
126
127
#ifdef R_MEMORY_PROFILING
128
129
attribute_hidden SEXP do_tracemem(SEXP call, SEXP op, SEXP args, SEXP rho)
130
{
131
    SEXP object;
132
    char buffer[21];
133
134
    checkArity(op, args);
135
    check1arg(args, call, "x");
136
137
    object = CAR(args);
138
    if (TYPEOF(object) == CLOSXP ||
139
  TYPEOF(object) == BUILTINSXP ||
140
  TYPEOF(object) == SPECIALSXP)
141
  errorcall(call, _("argument must not be a function"));
142
143
    if(object == R_NilValue)
144
  errorcall(call, _("cannot trace NULL"));
145
146
    if(TYPEOF(object) == ENVSXP || TYPEOF(object) == PROMSXP)
147
  errorcall(call,
148
      _("'tracemem' is not useful for promise and environment objects"));
149
    if(TYPEOF(object) == EXTPTRSXP || TYPEOF(object) == WEAKREFSXP)
150
  errorcall(call,
151
      _("'tracemem' is not useful for weak reference or external pointer objects"));
152
153
    SET_RTRACE(object, 1);
154
    snprintf(buffer, 21, "<%p>", (void *) object);
155
    return mkString(buffer);
156
}
157
158
attribute_hidden SEXP do_untracemem(SEXP call, SEXP op, SEXP args, SEXP rho)
159
{
160
    SEXP object;
161
162
    checkArity(op, args);
163
    check1arg(args, call, "x");
164
165
    object=CAR(args);
166
    if (TYPEOF(object) == CLOSXP ||
167
  TYPEOF(object) == BUILTINSXP ||
168
  TYPEOF(object) == SPECIALSXP)
169
  errorcall(call, _("argument must not be a function"));
170
171
    if (RTRACE(object))
172
  SET_RTRACE(object, 0);
173
    return R_NilValue;
174
}
175
176
#else
177
178
NORET attribute_hidden SEXP do_tracemem(SEXP call, SEXP op, SEXP args, SEXP rho)
179
0
{
180
0
    checkArity(op, args);
181
0
    check1arg(args, call, "x");
182
0
    errorcall(call, _("R was not compiled with support for memory profiling"));
183
0
}
184
185
NORET attribute_hidden SEXP do_untracemem(SEXP call, SEXP op, SEXP args, SEXP rho)
186
0
{
187
0
    checkArity(op, args);
188
0
    check1arg(args, call, "x");
189
0
    errorcall(call, _("R was not compiled with support for memory profiling"));
190
0
}
191
192
#endif /* R_MEMORY_PROFILING */
193
194
#ifndef R_MEMORY_PROFILING
195
0
attribute_hidden void memtrace_report(void* old, void *_new) {
196
0
    return;
197
0
}
198
#else
199
static void memtrace_stack_dump(void)
200
{
201
    RCNTXT *cptr;
202
203
    for (cptr = R_GlobalContext; cptr; cptr = cptr->nextcontext) {
204
  if ((cptr->callflag & (CTXT_FUNCTION | CTXT_BUILTIN))
205
      && TYPEOF(cptr->call) == LANGSXP) {
206
      SEXP fun = CAR(cptr->call);
207
      Rprintf("%s ",
208
        TYPEOF(fun) == SYMSXP ? EncodeChar(PRINTNAME(fun)) :
209
        "<Anonymous>");
210
  }
211
    }
212
    Rprintf("\n");
213
}
214
215
void memtrace_report(void * old, void * _new)
216
{
217
    if (!R_current_trace_state()) return;
218
    Rprintf("tracemem[%p -> %p]: ", (void *) old, _new);
219
    memtrace_stack_dump();
220
}
221
222
#endif /* R_MEMORY_PROFILING */
223
224
attribute_hidden SEXP do_retracemem(SEXP call, SEXP op, SEXP args, SEXP rho)
225
0
{
226
#ifdef R_MEMORY_PROFILING
227
    SEXP object, previous, ans, argList;
228
    char buffer[21];
229
    static SEXP do_retracemem_formals = NULL;
230
    Rboolean visible; 
231
232
    if (do_retracemem_formals == NULL)
233
  do_retracemem_formals = allocFormalsList2(install("x"),
234
              R_PreviousSymbol);
235
236
    PROTECT(argList =  matchArgs_NR(do_retracemem_formals, args, call));
237
    if(CAR(argList) == R_MissingArg) SETCAR(argList, R_NilValue);
238
    if(CADR(argList) == R_MissingArg) SETCAR(CDR(argList), R_NilValue);
239
240
    object = CAR(argList);
241
    if (TYPEOF(object) == CLOSXP ||
242
  TYPEOF(object) == BUILTINSXP ||
243
  TYPEOF(object) == SPECIALSXP)
244
  errorcall(call, _("argument must not be a function"));
245
246
    previous = CADR(argList);
247
    if(!isNull(previous) && (!isString(previous) || LENGTH(previous) != 1))
248
      errorcall(call, _("invalid '%s' argument"), "previous");
249
250
    if (RTRACE(object)) {
251
  snprintf(buffer, 21, "<%p>", (void *) object);
252
  visible = TRUE;
253
  ans = mkString(buffer);
254
    } else {
255
  visible = FALSE;
256
  ans = R_NilValue;
257
    }
258
259
    if (previous != R_NilValue){
260
  SET_RTRACE(object, 1);
261
  if (R_current_trace_state()) {
262
      /* FIXME: previous will have <0x....> whereas other values are
263
         without the < > */
264
      Rprintf("tracemem[%s -> %p]: ",
265
        translateChar(STRING_ELT(previous, 0)), (void *) object);
266
      memtrace_stack_dump();
267
  }
268
    }
269
    UNPROTECT(1);
270
    R_Visible = visible;
271
    return ans;
272
#else
273
0
    R_Visible = FALSE; /* for consistency with other case */
274
0
    return R_NilValue;
275
0
#endif
276
0
}