File: rp_measurement.c

package info (click to toggle)
ruby-prof 1.7.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,048 kB
  • sloc: ruby: 9,805; ansic: 2,968; makefile: 7
file content (364 lines) | stat: -rw-r--r-- 11,288 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
/* Copyright (C) 2005-2019 Shugo Maeda <shugo@ruby-lang.org> and Charlie Savage <cfis@savagexi.com>
   Please see the LICENSE file for copyright and distribution information */

#include "rp_measurement.h"

VALUE mMeasure;
VALUE cRpMeasurement;

prof_measurer_t* prof_measurer_allocations(bool track_allocations);
prof_measurer_t* prof_measurer_memory(bool track_allocations);
prof_measurer_t* prof_measurer_process_time(bool track_allocations);
prof_measurer_t* prof_measurer_wall_time(bool track_allocations);

void rp_init_measure_allocations(void);
void rp_init_measure_memory(void);
void rp_init_measure_process_time(void);
void rp_init_measure_wall_time(void);

prof_measurer_t* prof_measurer_create(prof_measure_mode_t measure, bool track_allocations)
{
    switch (measure)
    {
    case MEASURE_WALL_TIME:
        return prof_measurer_wall_time(track_allocations);
    case MEASURE_PROCESS_TIME:
        return prof_measurer_process_time(track_allocations);
    case MEASURE_ALLOCATIONS:
        return prof_measurer_allocations(track_allocations);
    case MEASURE_MEMORY:
        return prof_measurer_memory(track_allocations);
    default:
        rb_raise(rb_eArgError, "Unknown measure mode: %d", measure);
    }
};

double prof_measure(prof_measurer_t* measurer, rb_trace_arg_t* trace_arg)
{
    double measurement = measurer->measure(trace_arg);
    return measurement * measurer->multiplier;
}

/* =======  prof_measurement_t   ========*/
prof_measurement_t* prof_measurement_create(void)
{
    prof_measurement_t* result = ALLOC(prof_measurement_t);
    result->owner = OWNER_C;
    result->total_time = 0;
    result->self_time = 0;
    result->wait_time = 0;
    result->called = 0;
    result->object = Qnil;
    return result;
}

/* call-seq:
     new(total_time, self_time, wait_time, called) -> Measurement

   Creates a new measuremen instance. */
static VALUE prof_measurement_initialize(VALUE self, VALUE total_time, VALUE self_time, VALUE wait_time, VALUE called)
{
  prof_measurement_t* result = prof_get_measurement(self);

  result->total_time = NUM2DBL(total_time);
  result->self_time = NUM2DBL(self_time);
  result->wait_time = NUM2DBL(wait_time);
  result->called = NUM2INT(called);
  result->object = self;
  return self;
}

prof_measurement_t* prof_measurement_copy(prof_measurement_t* other)
{
  prof_measurement_t* result = prof_measurement_create();
  result->called = other->called;
  result->total_time = other->total_time;
  result->self_time = other->self_time;
  result->wait_time = other->wait_time;

  return result;
}

static VALUE prof_measurement_initialize_copy(VALUE self, VALUE other)
{
  // This object was created by Ruby either via Measurment#clone or Measurement#dup 
  // and thus prof_measurement_allocate was called so the object is owned by Ruby

  if (self == other)
    return self;

  prof_measurement_t* self_ptr = prof_get_measurement(self);
  prof_measurement_t* other_ptr = prof_get_measurement(other);

  self_ptr->called = other_ptr->called;
  self_ptr->total_time = other_ptr->total_time;
  self_ptr->self_time = other_ptr->self_time;
  self_ptr->wait_time = other_ptr->wait_time;

  return self;
}

void prof_measurement_mark(void* data)
{
    if (!data) return;

    prof_measurement_t* measurement = (prof_measurement_t*)data;

    if (measurement->object != Qnil)
        rb_gc_mark_movable(measurement->object);
}

void prof_measurement_compact(void* data)
{
    prof_measurement_t* measurement = (prof_measurement_t*)data;
    measurement->object = rb_gc_location(measurement->object);
}

void prof_measurement_free(prof_measurement_t* measurement)
{
    /* Has this measurement object been accessed by Ruby?  If
       yes clean it up so to avoid a segmentation fault. */
    if (measurement->object != Qnil)
    {
        RTYPEDDATA(measurement->object)->data = NULL;
        measurement->object = Qnil;
    }

    xfree(measurement);
}

static void prof_measurement_ruby_gc_free(void* data)
{
  prof_measurement_t* measurement = (prof_measurement_t*)data;

  if (!measurement)
  {
    // Object has already been freed by C code
    return;
  }
  else if (measurement->owner == OWNER_RUBY)
  {
    // Ruby owns this object, we need to free the underlying C struct
    prof_measurement_free(measurement);
  }
  else
  {
    // The Ruby object is being freed, but not the underlying C structure. So unlink the two.
    measurement->object = Qnil;
  }
}

size_t prof_measurement_size(const void* data)
{
    return sizeof(prof_measurement_t);
}

static const rb_data_type_t measurement_type =
{
    .wrap_struct_name = "Measurement",
    .function =
    {
        .dmark = prof_measurement_mark,
        .dfree = prof_measurement_ruby_gc_free,
        .dsize = prof_measurement_size,
        .dcompact = prof_measurement_compact
    },
    .data = NULL,
    .flags = RUBY_TYPED_FREE_IMMEDIATELY
};

VALUE prof_measurement_wrap(prof_measurement_t* measurement)
{
    if (measurement->object == Qnil)
    {
        measurement->object = TypedData_Wrap_Struct(cRpMeasurement, &measurement_type, measurement);
    }
    return measurement->object;
}

static VALUE prof_measurement_allocate(VALUE klass)
{
    prof_measurement_t* measurement = prof_measurement_create();
    // This object is being created by Ruby
    measurement->owner = OWNER_RUBY;
    measurement->object = prof_measurement_wrap(measurement);
    return measurement->object;
}

prof_measurement_t* prof_get_measurement(VALUE self)
{
    /* Can't use Data_Get_Struct because that triggers the event hook
       ending up in endless recursion. */
    prof_measurement_t* result = RTYPEDDATA_DATA(self);

    if (!result)
        rb_raise(rb_eRuntimeError, "This RubyProf::Measurement instance has already been freed, likely because its profile has been freed.");

    return result;
}

/* call-seq:
   total_time -> float

Returns the total amount of time spent in this method and its children. */
static VALUE prof_measurement_total_time(VALUE self)
{
    prof_measurement_t* result = prof_get_measurement(self);
    return rb_float_new(result->total_time);
}

/* call-seq:
   total_time=value -> value

Sets the call count to n. */
static VALUE prof_measurement_set_total_time(VALUE self, VALUE value)
{
  prof_measurement_t* result = prof_get_measurement(self);
  result->total_time = NUM2DBL(value);
  return value;
}

/* call-seq:
   self_time -> float

Returns the total amount of time spent in this method. */
static VALUE
prof_measurement_self_time(VALUE self)
{
    prof_measurement_t* result = prof_get_measurement(self);

    return rb_float_new(result->self_time);
}

/* call-seq:
   self_time=value -> value

Sets the call count to value. */
static VALUE prof_measurement_set_self_time(VALUE self, VALUE value)
{
  prof_measurement_t* result = prof_get_measurement(self);
  result->self_time = NUM2DBL(value);
  return value;
}

/* call-seq:
   wait_time -> float

Returns the total amount of time this method waited for other threads. */
static VALUE prof_measurement_wait_time(VALUE self)
{
    prof_measurement_t* result = prof_get_measurement(self);

    return rb_float_new(result->wait_time);
}

/* call-seq:
   wait_time=value -> value

Sets the wait time to value. */
static VALUE prof_measurement_set_wait_time(VALUE self, VALUE value)
{
  prof_measurement_t* result = prof_get_measurement(self);
  result->wait_time = NUM2DBL(value);
  return value;
}

/* call-seq:
   called -> int

Returns the total amount of times this method was called. */
static VALUE prof_measurement_called(VALUE self)
{
    prof_measurement_t* result = prof_get_measurement(self);
    return INT2NUM(result->called);
}

/* call-seq:
   called=value -> value

Sets the call count to value. */
static VALUE prof_measurement_set_called(VALUE self, VALUE value)
{
  prof_measurement_t* result = prof_get_measurement(self);
  result->called = NUM2INT(value);
  return value;
}

/* :nodoc: */
void prof_measurement_merge_internal(prof_measurement_t* self, prof_measurement_t* other)
{
  self->called += other->called;
  self->total_time += other->total_time;
  self->self_time += other->self_time;
  self->wait_time += other->wait_time;
}

/* call-seq:
   merge(other)

   Adds the content of other measurement to this measurement */
VALUE prof_measurement_merge(VALUE self, VALUE other)
{
  prof_measurement_t* self_ptr = prof_get_measurement(self);
  prof_measurement_t* other_ptr = prof_get_measurement(other);
  prof_measurement_merge_internal(self_ptr, other_ptr);
  return self;
}

/* :nodoc: */
static VALUE prof_measurement_dump(VALUE self)
{
    prof_measurement_t* measurement_data = prof_get_measurement(self);
    VALUE result = rb_hash_new();

    rb_hash_aset(result, ID2SYM(rb_intern("owner")), INT2FIX(measurement_data->owner));
    rb_hash_aset(result, ID2SYM(rb_intern("total_time")), rb_float_new(measurement_data->total_time));
    rb_hash_aset(result, ID2SYM(rb_intern("self_time")), rb_float_new(measurement_data->self_time));
    rb_hash_aset(result, ID2SYM(rb_intern("wait_time")), rb_float_new(measurement_data->wait_time));
    rb_hash_aset(result, ID2SYM(rb_intern("called")), INT2FIX(measurement_data->called));

    return result;
}

/* :nodoc: */
static VALUE
prof_measurement_load(VALUE self, VALUE data)
{
    prof_measurement_t* measurement = prof_get_measurement(self);
    measurement->object = self;

    measurement->owner = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("owner"))));
    measurement->total_time = rb_num2dbl(rb_hash_aref(data, ID2SYM(rb_intern("total_time"))));
    measurement->self_time = rb_num2dbl(rb_hash_aref(data, ID2SYM(rb_intern("self_time"))));
    measurement->wait_time = rb_num2dbl(rb_hash_aref(data, ID2SYM(rb_intern("wait_time"))));
    measurement->called = FIX2INT(rb_hash_aref(data, ID2SYM(rb_intern("called"))));

    return data;
}

void rp_init_measure(void)
{
    mMeasure = rb_define_module_under(mProf, "Measure");
    rp_init_measure_wall_time();
    rp_init_measure_process_time();
    rp_init_measure_allocations();
    rp_init_measure_memory();

    cRpMeasurement = rb_define_class_under(mProf, "Measurement", rb_cObject);
    rb_define_alloc_func(cRpMeasurement, prof_measurement_allocate);

    rb_define_method(cRpMeasurement, "initialize", prof_measurement_initialize, 4);
    rb_define_method(cRpMeasurement, "initialize_copy", prof_measurement_initialize_copy, 1);
    rb_define_method(cRpMeasurement, "merge!", prof_measurement_merge, 1);
    rb_define_method(cRpMeasurement, "called", prof_measurement_called, 0);
    rb_define_method(cRpMeasurement, "called=", prof_measurement_set_called, 1);
    rb_define_method(cRpMeasurement, "total_time", prof_measurement_total_time, 0);
    rb_define_method(cRpMeasurement, "total_time=", prof_measurement_set_total_time, 1);
    rb_define_method(cRpMeasurement, "self_time", prof_measurement_self_time, 0);
    rb_define_method(cRpMeasurement, "self_time=", prof_measurement_set_self_time, 1);
    rb_define_method(cRpMeasurement, "wait_time", prof_measurement_wait_time, 0);
    rb_define_method(cRpMeasurement, "wait_time=", prof_measurement_set_wait_time, 1);

    rb_define_method(cRpMeasurement, "_dump_data", prof_measurement_dump, 0);
    rb_define_method(cRpMeasurement, "_load_data", prof_measurement_load, 1);
}