Ruby  2.5.0dev(2017-10-22revision60238)
objspace_dump.c
Go to the documentation of this file.
1 /**********************************************************************
2 
3  objspace_dump.c - Heap dumping ObjectSpace extender for MRI.
4 
5  $Author$
6  created at: Sat Oct 11 10:11:00 2013
7 
8  NOTE: This extension library is not expected to exist except C Ruby.
9 
10  All the files in this distribution are covered under the Ruby's
11  license (see the file COPYING).
12 
13 **********************************************************************/
14 
15 #include "internal.h"
16 #include "ruby/debug.h"
17 #include "ruby/io.h"
18 #include "gc.h"
19 #include "node.h"
20 #include "vm_core.h"
21 #include "objspace.h"
22 
23 static VALUE sym_output, sym_stdout, sym_string, sym_file;
24 static VALUE sym_full;
25 
26 struct dump_config {
30  int roots;
31  const char *root_category;
35  int full_heap;
36 };
37 
38 PRINTF_ARGS(static void dump_append(struct dump_config *, const char *, ...), 2, 3);
39 static void
40 dump_append(struct dump_config *dc, const char *format, ...)
41 {
42  va_list vl;
43  va_start(vl, format);
44 
45  if (dc->stream) {
46  vfprintf(dc->stream, format, vl);
47  }
48  else if (dc->string)
49  rb_str_vcatf(dc->string, format, vl);
50 
51  va_end(vl);
52 }
53 
54 static void
55 dump_append_string_value(struct dump_config *dc, VALUE obj)
56 {
57  long i;
58  char c;
59  const char *value;
60 
61  dump_append(dc, "\"");
62  for (i = 0, value = RSTRING_PTR(obj); i < RSTRING_LEN(obj); i++) {
63  switch ((c = value[i])) {
64  case '\\':
65  case '"':
66  dump_append(dc, "\\%c", c);
67  break;
68  case '\0':
69  dump_append(dc, "\\u0000");
70  break;
71  case '\b':
72  dump_append(dc, "\\b");
73  break;
74  case '\t':
75  dump_append(dc, "\\t");
76  break;
77  case '\f':
78  dump_append(dc, "\\f");
79  break;
80  case '\n':
81  dump_append(dc, "\\n");
82  break;
83  case '\r':
84  dump_append(dc, "\\r");
85  break;
86  default:
87  if (c <= 0x1f)
88  dump_append(dc, "\\u%04d", c);
89  else
90  dump_append(dc, "%c", c);
91  }
92  }
93  dump_append(dc, "\"");
94 }
95 
96 static void
97 dump_append_symbol_value(struct dump_config *dc, VALUE obj)
98 {
99  dump_append(dc, "{\"type\":\"SYMBOL\", \"value\":");
100  dump_append_string_value(dc, rb_sym2str(obj));
101  dump_append(dc, "}");
102 }
103 
104 static inline const char *
105 obj_type(VALUE obj)
106 {
107  switch (BUILTIN_TYPE(obj)) {
108 #define CASE_TYPE(type) case T_##type: return #type
109  CASE_TYPE(NONE);
110  CASE_TYPE(NIL);
111  CASE_TYPE(OBJECT);
112  CASE_TYPE(CLASS);
113  CASE_TYPE(ICLASS);
114  CASE_TYPE(MODULE);
115  CASE_TYPE(FLOAT);
116  CASE_TYPE(STRING);
117  CASE_TYPE(REGEXP);
118  CASE_TYPE(ARRAY);
119  CASE_TYPE(HASH);
120  CASE_TYPE(STRUCT);
121  CASE_TYPE(BIGNUM);
122  CASE_TYPE(FILE);
123  CASE_TYPE(FIXNUM);
124  CASE_TYPE(TRUE);
125  CASE_TYPE(FALSE);
126  CASE_TYPE(DATA);
127  CASE_TYPE(MATCH);
128  CASE_TYPE(SYMBOL);
129  CASE_TYPE(RATIONAL);
130  CASE_TYPE(COMPLEX);
131  CASE_TYPE(IMEMO);
132  CASE_TYPE(UNDEF);
133  CASE_TYPE(NODE);
134  CASE_TYPE(ZOMBIE);
135 #undef CASE_TYPE
136  }
137  return "UNKNOWN";
138 }
139 
140 static void
141 dump_append_special_const(struct dump_config *dc, VALUE value)
142 {
143  if (value == Qtrue) {
144  dump_append(dc, "true");
145  }
146  else if (value == Qfalse) {
147  dump_append(dc, "false");
148  }
149  else if (value == Qnil) {
150  dump_append(dc, "null");
151  }
152  else if (FIXNUM_P(value)) {
153  dump_append(dc, "%ld", FIX2LONG(value));
154  }
155  else if (FLONUM_P(value)) {
156  dump_append(dc, "%#g", RFLOAT_VALUE(value));
157  }
158  else if (SYMBOL_P(value)) {
159  dump_append_symbol_value(dc, value);
160  }
161  else {
162  dump_append(dc, "{}");
163  }
164 }
165 
166 static void
167 reachable_object_i(VALUE ref, void *data)
168 {
169  struct dump_config *dc = (struct dump_config *)data;
170 
171  if (dc->cur_obj_klass == ref)
172  return;
173 
174  if (dc->cur_obj_references == 0)
175  dump_append(dc, ", \"references\":[\"%p\"", (void *)ref);
176  else
177  dump_append(dc, ", \"%p\"", (void *)ref);
178 
179  dc->cur_obj_references++;
180 }
181 
182 static void
183 dump_append_string_content(struct dump_config *dc, VALUE obj)
184 {
185  dump_append(dc, ", \"bytesize\":%ld", RSTRING_LEN(obj));
186  if (!STR_EMBED_P(obj) && !STR_SHARED_P(obj) && (long)rb_str_capacity(obj) != RSTRING_LEN(obj))
187  dump_append(dc, ", \"capacity\":%"PRIuSIZE, rb_str_capacity(obj));
188 
189  if (is_ascii_string(obj)) {
190  dump_append(dc, ", \"value\":");
191  dump_append_string_value(dc, obj);
192  }
193 }
194 
195 static const char *
196 imemo_name(int imemo)
197 {
198  switch(imemo) {
199 #define TYPE_STR(t) case(imemo_##t): return #t
200  TYPE_STR(env);
201  TYPE_STR(cref);
202  TYPE_STR(svar);
203  TYPE_STR(throw_data);
204  TYPE_STR(ifunc);
205  TYPE_STR(memo);
206  TYPE_STR(ment);
207  TYPE_STR(iseq);
208  default:
209  return "unknown";
210 #undef TYPE_STR
211  }
212 }
213 
214 static void
215 dump_object(VALUE obj, struct dump_config *dc)
216 {
217  size_t memsize;
218  struct allocation_info *ainfo;
219  rb_io_t *fptr;
221  size_t n, i;
222 
223  if (SPECIAL_CONST_P(obj)) {
224  dump_append_special_const(dc, obj);
225  return;
226  }
227 
228  dc->cur_obj = obj;
229  dc->cur_obj_references = 0;
230  dc->cur_obj_klass = BUILTIN_TYPE(obj) == T_NODE ? 0 : RBASIC_CLASS(obj);
231 
232  if (dc->cur_obj == dc->string)
233  return;
234 
235  dump_append(dc, "{\"address\":\"%p\", \"type\":\"%s\"", (void *)obj, obj_type(obj));
236 
237  if (dc->cur_obj_klass)
238  dump_append(dc, ", \"class\":\"%p\"", (void *)dc->cur_obj_klass);
239  if (rb_obj_frozen_p(obj))
240  dump_append(dc, ", \"frozen\":true");
241 
242  switch (BUILTIN_TYPE(obj)) {
243  case T_NONE:
244  dump_append(dc, "}\n");
245  return;
246 
247  case T_NODE:
248  dump_append(dc, ", \"node_type\":\"%s\"", ruby_node_name(nd_type(obj)));
249  break;
250 
251  case T_IMEMO:
252  dump_append(dc, ", \"imemo_type\":\"%s\"", imemo_name(imemo_type(obj)));
253  break;
254 
255  case T_SYMBOL:
256  dump_append_string_content(dc, rb_sym2str(obj));
257  break;
258 
259  case T_STRING:
260  if (STR_EMBED_P(obj))
261  dump_append(dc, ", \"embedded\":true");
262  if (is_broken_string(obj))
263  dump_append(dc, ", \"broken\":true");
264  if (FL_TEST(obj, RSTRING_FSTR))
265  dump_append(dc, ", \"fstring\":true");
266  if (STR_SHARED_P(obj))
267  dump_append(dc, ", \"shared\":true");
268  else
269  dump_append_string_content(dc, obj);
270 
271  if (!ENCODING_IS_ASCII8BIT(obj))
272  dump_append(dc, ", \"encoding\":\"%s\"", rb_enc_name(rb_enc_from_index(ENCODING_GET(obj))));
273  break;
274 
275  case T_HASH:
276  dump_append(dc, ", \"size\":%"PRIuSIZE, (size_t)RHASH_SIZE(obj));
277  if (FL_TEST(obj, HASH_PROC_DEFAULT))
278  dump_append(dc, ", \"default\":\"%p\"", (void *)RHASH_IFNONE(obj));
279  break;
280 
281  case T_ARRAY:
282  dump_append(dc, ", \"length\":%ld", RARRAY_LEN(obj));
283  if (RARRAY_LEN(obj) > 0 && FL_TEST(obj, ELTS_SHARED))
284  dump_append(dc, ", \"shared\":true");
285  if (RARRAY_LEN(obj) > 0 && FL_TEST(obj, RARRAY_EMBED_FLAG))
286  dump_append(dc, ", \"embedded\":true");
287  break;
288 
289  case T_CLASS:
290  case T_MODULE:
291  if (dc->cur_obj_klass)
292  dump_append(dc, ", \"name\":\"%s\"", rb_class2name(obj));
293  break;
294 
295  case T_DATA:
296  if (RTYPEDDATA_P(obj))
297  dump_append(dc, ", \"struct\":\"%s\"", RTYPEDDATA_TYPE(obj)->wrap_struct_name);
298  break;
299 
300  case T_FLOAT:
301  dump_append(dc, ", \"value\":\"%g\"", RFLOAT_VALUE(obj));
302  break;
303 
304  case T_OBJECT:
305  dump_append(dc, ", \"ivars\":%u", ROBJECT_NUMIV(obj));
306  break;
307 
308  case T_FILE:
309  fptr = RFILE(obj)->fptr;
310  if (fptr)
311  dump_append(dc, ", \"fd\":%d", fptr->fd);
312  break;
313 
314  case T_ZOMBIE:
315  dump_append(dc, "}\n");
316  return;
317  }
318 
319  rb_objspace_reachable_objects_from(obj, reachable_object_i, dc);
320  if (dc->cur_obj_references > 0)
321  dump_append(dc, "]");
322 
323  if ((ainfo = objspace_lookup_allocation_info(obj))) {
324  dump_append(dc, ", \"file\":\"%s\", \"line\":%lu", ainfo->path, ainfo->line);
325  if (RTEST(ainfo->mid)) {
326  VALUE m = rb_sym2str(ainfo->mid);
327  dump_append(dc, ", \"method\":\"%s\"", RSTRING_PTR(m));
328  }
329  dump_append(dc, ", \"generation\":%"PRIuSIZE, ainfo->generation);
330  }
331 
332  if ((memsize = rb_obj_memsize_of(obj)) > 0)
333  dump_append(dc, ", \"memsize\":%"PRIuSIZE, memsize);
334 
335  if ((n = rb_obj_gc_flags(obj, flags, sizeof(flags))) > 0) {
336  dump_append(dc, ", \"flags\":{");
337  for (i=0; i<n; i++) {
338  dump_append(dc, "\"%s\":true", rb_id2name(flags[i]));
339  if (i != n-1) dump_append(dc, ", ");
340  }
341  dump_append(dc, "}");
342  }
343 
344  dump_append(dc, "}\n");
345 }
346 
347 static int
348 heap_i(void *vstart, void *vend, size_t stride, void *data)
349 {
350  struct dump_config *dc = (struct dump_config *)data;
351  VALUE v = (VALUE)vstart;
352  for (; v != (VALUE)vend; v += stride) {
353  if (dc->full_heap || RBASIC(v)->flags)
354  dump_object(v, dc);
355  }
356  return 0;
357 }
358 
359 static void
360 root_obj_i(const char *category, VALUE obj, void *data)
361 {
362  struct dump_config *dc = (struct dump_config *)data;
363 
364  if (dc->root_category != NULL && category != dc->root_category)
365  dump_append(dc, "]}\n");
366  if (dc->root_category == NULL || category != dc->root_category)
367  dump_append(dc, "{\"type\":\"ROOT\", \"root\":\"%s\", \"references\":[\"%p\"", category, (void *)obj);
368  else
369  dump_append(dc, ", \"%p\"", (void *)obj);
370 
371  dc->root_category = category;
372  dc->roots++;
373 }
374 
375 static VALUE
376 dump_output(struct dump_config *dc, VALUE opts, VALUE output, const char *filename)
377 {
378  VALUE tmp;
379 
380  dc->full_heap = 0;
381 
382  if (RTEST(opts)) {
383  output = rb_hash_aref(opts, sym_output);
384 
385  if (Qtrue == rb_hash_lookup2(opts, sym_full, Qfalse))
386  dc->full_heap = 1;
387  }
388 
389  if (output == sym_stdout) {
390  dc->stream = stdout;
391  dc->string = Qnil;
392  }
393  else if (output == sym_file) {
394  rb_io_t *fptr;
395  rb_require("tempfile");
396  tmp = rb_assoc_new(rb_str_new_cstr(filename), rb_str_new_cstr(".json"));
397  tmp = rb_funcallv(rb_path2class("Tempfile"), rb_intern("create"), 1, &tmp);
398  io:
399  dc->string = rb_io_get_write_io(tmp);
400  rb_io_flush(dc->string);
401  GetOpenFile(dc->string, fptr);
402  dc->stream = rb_io_stdio_file(fptr);
403  }
404  else if (output == sym_string) {
405  dc->string = rb_str_new_cstr("");
406  }
407  else if (!NIL_P(tmp = rb_io_check_io(output))) {
408  output = sym_file;
409  goto io;
410  }
411  else {
412  rb_raise(rb_eArgError, "wrong output option: %"PRIsVALUE, output);
413  }
414  return output;
415 }
416 
417 static VALUE
418 dump_result(struct dump_config *dc, VALUE output)
419 {
420  if (output == sym_string) {
421  return rb_str_resurrect(dc->string);
422  }
423  else if (output == sym_file) {
424  rb_io_flush(dc->string);
425  return dc->string;
426  }
427  else {
428  return Qnil;
429  }
430 }
431 
432 /*
433  * call-seq:
434  * ObjectSpace.dump(obj[, output: :string]) # => "{ ... }"
435  * ObjectSpace.dump(obj, output: :file) # => #<File:/tmp/rubyobj20131125-88733-1xkfmpv.json>
436  * ObjectSpace.dump(obj, output: :stdout) # => nil
437  *
438  * Dump the contents of a ruby object as JSON.
439  *
440  * This method is only expected to work with C Ruby.
441  * This is an experimental method and is subject to change.
442  * In particular, the function signature and output format are
443  * not guaranteed to be compatible in future versions of ruby.
444  */
445 
446 static VALUE
447 objspace_dump(int argc, VALUE *argv, VALUE os)
448 {
449  static const char filename[] = "rubyobj";
450  VALUE obj = Qnil, opts = Qnil, output;
451  struct dump_config dc = {0,};
452 
453  rb_scan_args(argc, argv, "1:", &obj, &opts);
454 
455  output = dump_output(&dc, opts, sym_string, filename);
456 
457  dump_object(obj, &dc);
458 
459  return dump_result(&dc, output);
460 }
461 
462 /*
463  * call-seq:
464  * ObjectSpace.dump_all([output: :file]) # => #<File:/tmp/rubyheap20131125-88469-laoj3v.json>
465  * ObjectSpace.dump_all(output: :stdout) # => nil
466  * ObjectSpace.dump_all(output: :string) # => "{...}\n{...}\n..."
467  * ObjectSpace.dump_all(output:
468  * File.open('heap.json','w')) # => #<File:heap.json>
469  *
470  * Dump the contents of the ruby heap as JSON.
471  *
472  * This method is only expected to work with C Ruby.
473  * This is an experimental method and is subject to change.
474  * In particular, the function signature and output format are
475  * not guaranteed to be compatible in future versions of ruby.
476  */
477 
478 static VALUE
479 objspace_dump_all(int argc, VALUE *argv, VALUE os)
480 {
481  static const char filename[] = "rubyheap";
482  VALUE opts = Qnil, output;
483  struct dump_config dc = {0,};
484 
485  rb_scan_args(argc, argv, "0:", &opts);
486 
487  output = dump_output(&dc, opts, sym_file, filename);
488 
489  /* dump roots */
491  if (dc.roots) dump_append(&dc, "]}\n");
492 
493  /* dump all objects */
494  rb_objspace_each_objects(heap_i, &dc);
495 
496  return dump_result(&dc, output);
497 }
498 
499 void
501 {
502 #if 0
503  rb_mObjSpace = rb_define_module("ObjectSpace"); /* let rdoc know */
504 #endif
505 
506  rb_define_module_function(rb_mObjSpace, "dump", objspace_dump, -1);
507  rb_define_module_function(rb_mObjSpace, "dump_all", objspace_dump_all, -1);
508 
509  sym_output = ID2SYM(rb_intern("output"));
510  sym_stdout = ID2SYM(rb_intern("stdout"));
511  sym_string = ID2SYM(rb_intern("string"));
512  sym_file = ID2SYM(rb_intern("file"));
513  sym_full = ID2SYM(rb_intern("full"));
514 
515  /* force create static IDs */
516  rb_obj_gc_flags(rb_mObjSpace, 0, 0);
517 }
#define ELTS_SHARED
Definition: ruby.h:937
FILE * stream
Definition: objspace_dump.c:28
#define T_SYMBOL
Definition: ruby.h:508
#define T_OBJECT
Definition: ruby.h:491
#define is_broken_string(str)
Definition: internal.h:1655
#define RARRAY_LEN(a)
Definition: ruby.h:1019
#define FALSE
Definition: nkf.h:174
#define RB_OBJ_GC_FLAGS_MAX
Definition: internal.h:1919
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2284
#define T_MODULE
Definition: ruby.h:494
#define Qtrue
Definition: ruby.h:437
#define is_ascii_string(str)
Definition: internal.h:1654
Definition: io.h:62
VALUE rb_io_flush(VALUE)
Definition: io.c:1592
#define MATCH(s)
#define UNDEF
void rb_objspace_reachable_objects_from_root(void(func)(const char *category, VALUE, void *), void *passing_data)
Definition: gc.c:7627
#define T_HASH
Definition: ruby.h:499
const char * root_category
Definition: objspace_dump.c:31
void rb_objspace_each_objects(each_obj_callback *callback, void *data)
Definition: gc.c:2489
#define T_ARRAY
Definition: ruby.h:498
#define RFILE(obj)
Definition: ruby.h:1206
#define ROBJECT_NUMIV(o)
Definition: ruby.h:900
#define FIXNUM_P(f)
Definition: ruby.h:365
VALUE rb_path2class(const char *)
Definition: variable.c:432
PRINTF_ARGS(static void dump_append(struct dump_config *, const char *,...), 2, 3)
#define FL_TEST(x, f)
Definition: ruby.h:1282
#define nd_type(n)
Definition: node.h:272
#define GetOpenFile(obj, fp)
Definition: io.h:120
#define RHASH_IFNONE(h)
Definition: ruby.h:1058
VALUE rb_eArgError
Definition: error.c:802
Definition: node.h:233
size_t generation
Definition: objspace.h:16
VALUE cur_obj_klass
Definition: objspace_dump.c:33
size_t rb_obj_memsize_of(VALUE obj)
Definition: gc.c:3327
#define TYPE_STR(t)
VALUE rb_require(const char *)
Definition: load.c:1061
VALUE rb_io_get_write_io(VALUE io)
Definition: io.c:670
VALUE rb_io_check_io(VALUE io)
Definition: io.c:664
#define NIL_P(v)
Definition: ruby.h:451
int fd
Definition: io.h:64
unsigned long line
Definition: objspace.h:13
#define FLONUM_P(x)
Definition: ruby.h:399
#define T_FLOAT
Definition: ruby.h:495
int argc
Definition: ruby.c:187
#define Qfalse
Definition: ruby.h:436
#define T_NODE
Definition: ruby.h:513
#define STR_SHARED_P(s)
Definition: internal.h:1653
#define HASH_PROC_DEFAULT
Definition: internal.h:1273
VALUE rb_str_resurrect(VALUE str)
Definition: string.c:1494
#define RSTRING_LEN(str)
Definition: ruby.h:971
void rb_define_module_function(VALUE module, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a module function for module.
Definition: class.c:1731
#define TRUE
Definition: nkf.h:175
#define T_DATA
Definition: ruby.h:506
#define rb_enc_name(enc)
Definition: encoding.h:171
size_t rb_obj_gc_flags(VALUE obj, ID *flags, size_t max)
Definition: gc.c:6142
VALUE rb_str_vcatf(VALUE, const char *, va_list)
Definition: sprintf.c:1465
#define RHASH_SIZE(hsh)
Definition: fbuffer.h:8
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Definition: class.c:1908
#define T_IMEMO
Definition: ruby.h:511
VALUE rb_assoc_new(VALUE car, VALUE cdr)
Definition: array.c:639
#define PRIsVALUE
Definition: ruby.h:135
unsigned long ID
Definition: ruby.h:86
VALUE rb_obj_frozen_p(VALUE obj)
call-seq: obj.frozen? -> true or false
Definition: object.c:1360
#define Qnil
Definition: ruby.h:438
#define BUILTIN_TYPE(x)
Definition: ruby.h:518
unsigned long VALUE
Definition: ruby.h:85
#define RBASIC(obj)
Definition: ruby.h:1197
const char * rb_class2name(VALUE)
Definition: variable.c:450
const char * rb_id2name(ID)
Definition: symbol.c:751
void Init_objspace_dump(VALUE rb_mObjSpace)
VALUE rb_str_new_cstr(const char *)
Definition: string.c:771
#define ENCODING_IS_ASCII8BIT(obj)
Definition: encoding.h:59
#define RTYPEDDATA_P(v)
Definition: ruby.h:1108
#define rb_funcallv
Definition: console.c:21
#define RSTRING_PTR(str)
Definition: ruby.h:975
struct allocation_info * objspace_lookup_allocation_info(VALUE obj)
#define ENCODING_GET(obj)
Definition: encoding.h:58
#define RFLOAT_VALUE(v)
Definition: ruby.h:933
VALUE rb_hash_lookup2(VALUE hash, VALUE key, VALUE def)
Definition: hash.c:842
void rb_objspace_reachable_objects_from(VALUE obj, void(func)(VALUE, void *), void *data)
Definition: gc.c:7599
#define RBASIC_CLASS(obj)
Definition: ruby.h:878
VALUE rb_hash_aref(VALUE hash, VALUE key)
Definition: hash.c:831
const char * ruby_node_name(int node)
Definition: iseq.c:1767
#define RTEST(v)
Definition: ruby.h:450
#define T_STRING
Definition: ruby.h:496
#define PRIuSIZE
Definition: ruby.h:177
size_t rb_str_capacity(VALUE str)
Definition: string.c:675
#define T_FILE
Definition: ruby.h:502
FILE * rb_io_stdio_file(rb_io_t *fptr)
Definition: io.c:7676
#define T_CLASS
Definition: ruby.h:492
#define ID2SYM(x)
Definition: ruby.h:383
imemo_type
Definition: internal.h:838
#define CASE_TYPE(type)
#define SPECIAL_CONST_P(x)
Definition: ruby.h:1242
VALUE rb_define_module(const char *name)
Definition: class.c:768
#define rb_intern(str)
#define T_ZOMBIE
Definition: ruby.h:514
#define SYMBOL_P(x)
Definition: ruby.h:382
size_t cur_obj_references
Definition: objspace_dump.c:34
VALUE flags
Definition: objspace.h:8
#define T_NONE
Definition: ruby.h:489
#define env
#define NULL
Definition: _sdbm.c:102
#define RTYPEDDATA_TYPE(v)
Definition: ruby.h:1109
#define FIX2LONG(x)
Definition: ruby.h:363
const char * path
Definition: objspace.h:12
#define STR_EMBED_P(str)
Definition: internal.h:1652
char ** argv
Definition: ruby.c:188
rb_encoding * rb_enc_from_index(int index)
Definition: encoding.c:616
#define rb_sym2str(sym)
Definition: console.c:107