Skip to content

Commit

Permalink
Include specific kind of VM-internal T_IMEMO object in allocation info
Browse files Browse the repository at this point in the history
**What does this PR do?**

This PR extends the code that generates a description for an object
during allocation to include the specific kind of `T_IMEMO` object.

On Ruby < 3, the `rb_imemo_name` function we're relying on does not
exist, so I chose to just not gather this information for legacy Rubies.
(It's posssible to still get it, but I didn't want to spend more time).

**Motivation:**

During discussion of DataDog#3282 the question of what a `T_IMEMO` is came
up, and since it turns out it can represent different kinds of things
in the Ruby VM, I decided to see if there was a way of spelling those
out, and it turns out there is.

Is this going to be useful for most customers? Probably not, but even
for our internal debugging/learning, it may come in handy, so it was
a bit of a "why not" kinda thing.

**Additional Notes:**

N/A

**How to test the change?**

I hesitated in terms of testing this, since these objects are internally
created by the VM so we could only run a bit of code and kinda hope to
trigger their creation.

Since this was a kind-of small thing I... decided to just leave it
as-is without a specific test.

In the future, we may add some testing, if we think this feature is
useful enough.

I did test it locally -- on Ruby 3.x I got from this trivial script
`DD_PROFILING_ENABLED=true bundle exec ddtracerb exec ruby -e "def foo; end; foo; sleep(1)"`
(output from `go pprof tool`):

```
allocation class:[(VM Internal, T_IMEMO, callcache)] ruby vm type:[T_IMEMO] thread id:[649074 (2660)] thread name:[main]
allocation class:[(VM Internal, T_IMEMO, constcache)] ruby vm type:[T_IMEMO] thread id:[649074 (2660)] thread name:[main]
allocation class:[(VM Internal, T_IMEMO, ment)] ruby vm type:[T_IMEMO] thread id:[649074 (2660)] thread name:[main]
```

whereas on Ruby 2.x, I got the `(VM Internal, T_IMEMO)` only.
  • Loading branch information
ivoanjo committed Nov 29, 2023
1 parent 2d4dad6 commit ffc9f36
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -1197,7 +1197,14 @@ void thread_context_collector_sample_allocation(VALUE self_instance, unsigned in
class_name = ruby_value_type_to_class_name(type);
}
} else if (type == RUBY_T_IMEMO) {
class_name = DDOG_CHARSLICE_C("(VM Internal, T_IMEMO)");
const char *imemo_string = imemo_kind(new_object);
if (imemo_string != NULL) {
char imemo_type[100];
snprintf(imemo_type, 100, "(VM Internal, T_IMEMO, %s)", imemo_string);
class_name = (ddog_CharSlice) {.ptr = imemo_type, .len = strlen(imemo_type)};
} else { // Ruby < 3
class_name = DDOG_CHARSLICE_C("(VM Internal, T_IMEMO)");
}
} else {
class_name = ruby_vm_type; // For other weird internal things we just use the VM type
}
Expand Down
3 changes: 3 additions & 0 deletions ext/ddtrace_profiling_native_extension/extconf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,9 @@ def add_compiler_flag(flag)
# On older Rubies, there are no Ractors
$defs << '-DNO_RACTORS' if RUBY_VERSION < '3'

# On older Rubies, rb_imemo_name did not exist
$defs << '-DNO_IMEMO_NAME' if RUBY_VERSION < '3'

# On older Rubies, objects would not move
$defs << '-DNO_T_MOVED' if RUBY_VERSION < '2.7'

Expand Down
27 changes: 27 additions & 0 deletions ext/ddtrace_profiling_native_extension/private_vm_api_access.c
Original file line number Diff line number Diff line change
Expand Up @@ -827,3 +827,30 @@ void self_test_mn_enabled(void) {
}
#endif
}

// Taken from upstream imemo.h at commit 6ebcf25de2859b5b6402b7e8b181066c32d0e0bf (November 2023, master branch)
// (See the Ruby project copyright and license above)
// to enable calling rb_imemo_name
//
// Modifications:
// * Added IMEMO_MASK define
// * Changed return type to int to avoid having to define `enum imemo_type`
static inline int ddtrace_imemo_type(VALUE imemo) {
// This mask is the same between Ruby 2.5 and 3.3-preview3. Furthermore, the intention of this method is to be used
// to call `rb_imemo_name` which correctly handles invalid numbers so even if the mask changes in the future, at most
// we'll get incorrect results (and never a VM crash)
#define IMEMO_MASK 0x0f
return (RBASIC(imemo)->flags >> FL_USHIFT) & IMEMO_MASK;
}

// Safety: This function assumes the object passed in is of the imemo type. But in the worst case, you'll just get
// a string that doesn't make any sense.
#ifndef NO_IMEMO_NAME
const char *imemo_kind(VALUE imemo) {
return rb_imemo_name(ddtrace_imemo_type(imemo));
}
#else
const char *imemo_kind(__attribute__((unused)) VALUE imemo) {
return NULL;
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,6 @@ VALUE invoke_location_for(VALUE thread, int *line_location);

// Check if RUBY_MN_THREADS is enabled (aka main Ractor is not doing 1:1 threads)
void self_test_mn_enabled(void);

// Provides more specific information on what kind an imemo is
const char *imemo_kind(VALUE imemo);

0 comments on commit ffc9f36

Please sign in to comment.