c_location 0.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +63 -0
- data/example/c_extension_location.rb +6 -0
- data/example/inbuilt_location.rb +6 -0
- data/ext/c_location.c +1 -104
- data/ext/c_location.h +114 -0
- metadata +5 -3
data/README.md
CHANGED
@@ -0,0 +1,63 @@
|
|
1
|
+
Given various assumptions about your setup, provides `Method#c_location`, which is the
|
2
|
+
equivalent of `Method#source_location` for methods that were written in C.
|
3
|
+
|
4
|
+
Requirements
|
5
|
+
============
|
6
|
+
|
7
|
+
On Mac OS X, you will need the `nm` tool that's bundled with Xcode; and the `gobjdump`
|
8
|
+
tool that you can get with `brew install binutils`.
|
9
|
+
|
10
|
+
On Linux you'll need the `objdump` tool, on Debian/Ubuntu it's obtainable with `apt-get install
|
11
|
+
binutils`, I assume other distros are similar.
|
12
|
+
|
13
|
+
On all systems you'll need your Rubies compiled with debug information (seems to be the
|
14
|
+
default on Linux). If you're using `rvm` add `export rvm_configure_flags="CFLAGS=-g"` to
|
15
|
+
`~/.rvmrc`.
|
16
|
+
|
17
|
+
If it still doesn't work — try running the commands that are found in
|
18
|
+
`lib/c_location/resolver.rb` manually, and see where the problem is.
|
19
|
+
|
20
|
+
|
21
|
+
How does this even work?!
|
22
|
+
=========================
|
23
|
+
|
24
|
+
1. Find a pointer to the C function that's behind a ruby method (most of the code in
|
25
|
+
`ext/c_location.c` deals with doing this for various versions of Ruby — it's certainly
|
26
|
+
not exposed in the API!)
|
27
|
+
|
28
|
+
2. Find the shared object file that contains this C function, and the offset in that file
|
29
|
+
that the function's compiled code resides. This is done using the non-standard (but
|
30
|
+
widespread) `dladdr()` function. (See man `dladdr`).
|
31
|
+
|
32
|
+
3. (Mac OS X only). Use `nm` to find the original library that the shared object file was
|
33
|
+
built from. (This is because `.bundle` files don't contain the debugging information
|
34
|
+
directly).
|
35
|
+
|
36
|
+
4. use `objdump` to read the DWARF data embedded in the shared object file when it was
|
37
|
+
compiled with the `-g` flag.
|
38
|
+
|
39
|
+
5. Use some heuristics to guess the directories that might include that file based on the
|
40
|
+
location of the object file on disk, and look for a correctly named file there.
|
41
|
+
|
42
|
+
(In short, lots and lots of string).
|
43
|
+
|
44
|
+
|
45
|
+
TODO
|
46
|
+
====
|
47
|
+
|
48
|
+
If possible, it'd be nice to have fewer external calls. Perhaps the data we're using `nm`
|
49
|
+
for could be done in Ruby without too much help; we could even try bundling `libdwarf` to
|
50
|
+
avoid the dependency on `objdump`.
|
51
|
+
|
52
|
+
It'd be really cool to make the Pry `edit-method` command work for C extensions. I think
|
53
|
+
this is just a matter of writing more code: have `edit-method` work as normal, and then
|
54
|
+
after closing the editor run `make` in the right directory; then copy the `.so` file into
|
55
|
+
a new directory and then require it. (the Init_foo method should then just overwrite all
|
56
|
+
the existing setup).
|
57
|
+
|
58
|
+
Meta-fu
|
59
|
+
=======
|
60
|
+
|
61
|
+
Released under the MIT License. Bug reports and pull requests welcome.
|
62
|
+
|
63
|
+
Do not use this in production :p.
|
data/ext/c_location.c
CHANGED
@@ -1,15 +1,4 @@
|
|
1
|
-
|
2
|
-
#include <dlfcn.h>
|
3
|
-
|
4
|
-
#include "ruby.h"
|
5
|
-
|
6
|
-
// TODO: why is it necessary to define this here? It ought to be in <dlfcn.h>
|
7
|
-
typedef struct {
|
8
|
-
const char *dli_fname; /* Pathname of shared object that contains address */
|
9
|
-
void *dli_fbase; /* Address at which shared object is loaded */
|
10
|
-
const char *dli_sname; /* Name of nearest symbol with address lower than addr */
|
11
|
-
void *dli_saddr; /* Exact address of symbol named in dli_sname */
|
12
|
-
} Dl2_info;
|
1
|
+
#include "c_location.h"
|
13
2
|
|
14
3
|
/* Given the pointer to a C function (called "name" for error handling)
|
15
4
|
* return the filename and offset of the compiled byte code for that function.
|
@@ -50,78 +39,8 @@ Init_c_location()
|
|
50
39
|
|
51
40
|
#ifdef RUBY_19
|
52
41
|
|
53
|
-
typedef enum {
|
54
|
-
NOEX_PUBLIC = 0x00,
|
55
|
-
NOEX_NOSUPER = 0x01,
|
56
|
-
NOEX_PRIVATE = 0x02,
|
57
|
-
NOEX_PROTECTED = 0x04,
|
58
|
-
NOEX_MASK = 0x06,
|
59
|
-
NOEX_BASIC = 0x08,
|
60
|
-
NOEX_UNDEF = NOEX_NOSUPER,
|
61
|
-
NOEX_MODFUNC = 0x12,
|
62
|
-
NOEX_SUPER = 0x20,
|
63
|
-
NOEX_VCALL = 0x40,
|
64
|
-
NOEX_RESPONDS = 0x80
|
65
|
-
} rb_method_flag_t;
|
66
|
-
|
67
|
-
typedef enum {
|
68
|
-
VM_METHOD_TYPE_ISEQ,
|
69
|
-
VM_METHOD_TYPE_CFUNC,
|
70
|
-
VM_METHOD_TYPE_ATTRSET,
|
71
|
-
VM_METHOD_TYPE_IVAR,
|
72
|
-
VM_METHOD_TYPE_BMETHOD,
|
73
|
-
VM_METHOD_TYPE_ZSUPER,
|
74
|
-
VM_METHOD_TYPE_UNDEF,
|
75
|
-
VM_METHOD_TYPE_NOTIMPLEMENTED,
|
76
|
-
VM_METHOD_TYPE_OPTIMIZED, /* Kernel#send, Proc#call, etc */
|
77
|
-
VM_METHOD_TYPE_MISSING /* wrapper for method_missing(id) */
|
78
|
-
} rb_method_type_t;
|
79
|
-
|
80
|
-
typedef struct rb_method_cfunc_struct {
|
81
|
-
VALUE (*func)(ANYARGS);
|
82
|
-
int argc;
|
83
|
-
} rb_method_cfunc_t;
|
84
|
-
|
85
|
-
typedef struct rb_method_attr_struct {
|
86
|
-
ID id;
|
87
|
-
VALUE location;
|
88
|
-
} rb_method_attr_t;
|
89
|
-
|
90
|
-
typedef struct rb_iseq_struct rb_iseq_t;
|
91
|
-
typedef struct rb_method_definition_struct {
|
92
|
-
rb_method_type_t type; /* method type */
|
93
|
-
ID original_id;
|
94
|
-
union {
|
95
|
-
rb_iseq_t *iseq; /* should be mark */
|
96
|
-
rb_method_cfunc_t cfunc;
|
97
|
-
rb_method_attr_t attr;
|
98
|
-
VALUE proc; /* should be mark */
|
99
|
-
enum method_optimized_type {
|
100
|
-
OPTIMIZED_METHOD_TYPE_SEND,
|
101
|
-
OPTIMIZED_METHOD_TYPE_CALL
|
102
|
-
} optimize_type;
|
103
|
-
} body;
|
104
|
-
int alias_count;
|
105
|
-
} rb_method_definition_t;
|
106
|
-
|
107
|
-
typedef struct rb_method_entry_struct {
|
108
|
-
rb_method_flag_t flag;
|
109
|
-
char mark;
|
110
|
-
rb_method_definition_t *def;
|
111
|
-
ID called_id;
|
112
|
-
VALUE klass; /* should be mark */
|
113
|
-
} rb_method_entry_t;
|
114
|
-
|
115
42
|
#ifdef RUBY_193
|
116
43
|
|
117
|
-
struct METHOD {
|
118
|
-
VALUE recv;
|
119
|
-
VALUE rclass;
|
120
|
-
ID id;
|
121
|
-
rb_method_entry_t *me;
|
122
|
-
struct unlinked_method_entry_list_entry *ume;
|
123
|
-
};
|
124
|
-
|
125
44
|
static VALUE compiled_location(VALUE self)
|
126
45
|
{
|
127
46
|
struct METHOD *data;
|
@@ -142,14 +61,6 @@ static VALUE compiled_location(VALUE self)
|
|
142
61
|
|
143
62
|
#else /* RUBY_192 */
|
144
63
|
|
145
|
-
struct METHOD {
|
146
|
-
VALUE recv;
|
147
|
-
VALUE rclass;
|
148
|
-
ID id;
|
149
|
-
rb_method_entry_t me;
|
150
|
-
struct unlinked_method_entry_list_entry *ume;
|
151
|
-
};
|
152
|
-
|
153
64
|
static VALUE compiled_location(VALUE self)
|
154
65
|
{
|
155
66
|
struct METHOD *data;
|
@@ -170,20 +81,8 @@ static VALUE compiled_location(VALUE self)
|
|
170
81
|
|
171
82
|
|
172
83
|
#endif
|
173
|
-
|
174
84
|
#else /* RUBY18 */
|
175
85
|
|
176
|
-
#include "node.h"
|
177
|
-
|
178
|
-
/* Copy-pasted out of Ruby 1.8.7, not part of the official C API.*/
|
179
|
-
struct METHOD {
|
180
|
-
VALUE klass, rklass;
|
181
|
-
VALUE recv;
|
182
|
-
ID id, oid;
|
183
|
-
int safe_level;
|
184
|
-
NODE *body;
|
185
|
-
};
|
186
|
-
|
187
86
|
static VALUE compiled_location(VALUE self)
|
188
87
|
{
|
189
88
|
struct METHOD *data;
|
@@ -197,6 +96,4 @@ static VALUE compiled_location(VALUE self)
|
|
197
96
|
|
198
97
|
return file_and_offset(*data->body->nd_cfnc, StringValueCStr(name));
|
199
98
|
}
|
200
|
-
|
201
|
-
|
202
99
|
#endif
|
data/ext/c_location.h
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
/* Load dladdr() function */
|
2
|
+
#include <dlfcn.h>
|
3
|
+
|
4
|
+
#include "ruby.h"
|
5
|
+
|
6
|
+
// TODO: why is it necessary to define this here? It ought to be in <dlfcn.h>
|
7
|
+
typedef struct {
|
8
|
+
const char *dli_fname; /* Pathname of shared object that contains address */
|
9
|
+
void *dli_fbase; /* Address at which shared object is loaded */
|
10
|
+
const char *dli_sname; /* Name of nearest symbol with address lower than addr */
|
11
|
+
void *dli_saddr; /* Exact address of symbol named in dli_sname */
|
12
|
+
} Dl2_info;
|
13
|
+
|
14
|
+
|
15
|
+
#ifdef RUBY_19
|
16
|
+
|
17
|
+
typedef enum {
|
18
|
+
NOEX_PUBLIC = 0x00,
|
19
|
+
NOEX_NOSUPER = 0x01,
|
20
|
+
NOEX_PRIVATE = 0x02,
|
21
|
+
NOEX_PROTECTED = 0x04,
|
22
|
+
NOEX_MASK = 0x06,
|
23
|
+
NOEX_BASIC = 0x08,
|
24
|
+
NOEX_UNDEF = NOEX_NOSUPER,
|
25
|
+
NOEX_MODFUNC = 0x12,
|
26
|
+
NOEX_SUPER = 0x20,
|
27
|
+
NOEX_VCALL = 0x40,
|
28
|
+
NOEX_RESPONDS = 0x80
|
29
|
+
} rb_method_flag_t;
|
30
|
+
|
31
|
+
typedef enum {
|
32
|
+
VM_METHOD_TYPE_ISEQ,
|
33
|
+
VM_METHOD_TYPE_CFUNC,
|
34
|
+
VM_METHOD_TYPE_ATTRSET,
|
35
|
+
VM_METHOD_TYPE_IVAR,
|
36
|
+
VM_METHOD_TYPE_BMETHOD,
|
37
|
+
VM_METHOD_TYPE_ZSUPER,
|
38
|
+
VM_METHOD_TYPE_UNDEF,
|
39
|
+
VM_METHOD_TYPE_NOTIMPLEMENTED,
|
40
|
+
VM_METHOD_TYPE_OPTIMIZED, /* Kernel#send, Proc#call, etc */
|
41
|
+
VM_METHOD_TYPE_MISSING /* wrapper for method_missing(id) */
|
42
|
+
} rb_method_type_t;
|
43
|
+
|
44
|
+
typedef struct rb_method_cfunc_struct {
|
45
|
+
VALUE (*func)(ANYARGS);
|
46
|
+
int argc;
|
47
|
+
} rb_method_cfunc_t;
|
48
|
+
|
49
|
+
typedef struct rb_method_attr_struct {
|
50
|
+
ID id;
|
51
|
+
VALUE location;
|
52
|
+
} rb_method_attr_t;
|
53
|
+
|
54
|
+
typedef struct rb_iseq_struct rb_iseq_t;
|
55
|
+
typedef struct rb_method_definition_struct {
|
56
|
+
rb_method_type_t type; /* method type */
|
57
|
+
ID original_id;
|
58
|
+
union {
|
59
|
+
rb_iseq_t *iseq; /* should be mark */
|
60
|
+
rb_method_cfunc_t cfunc;
|
61
|
+
rb_method_attr_t attr;
|
62
|
+
VALUE proc; /* should be mark */
|
63
|
+
enum method_optimized_type {
|
64
|
+
OPTIMIZED_METHOD_TYPE_SEND,
|
65
|
+
OPTIMIZED_METHOD_TYPE_CALL
|
66
|
+
} optimize_type;
|
67
|
+
} body;
|
68
|
+
int alias_count;
|
69
|
+
} rb_method_definition_t;
|
70
|
+
|
71
|
+
typedef struct rb_method_entry_struct {
|
72
|
+
rb_method_flag_t flag;
|
73
|
+
char mark;
|
74
|
+
rb_method_definition_t *def;
|
75
|
+
ID called_id;
|
76
|
+
VALUE klass; /* should be mark */
|
77
|
+
} rb_method_entry_t;
|
78
|
+
|
79
|
+
#ifdef RUBY_193
|
80
|
+
|
81
|
+
struct METHOD {
|
82
|
+
VALUE recv;
|
83
|
+
VALUE rclass;
|
84
|
+
ID id;
|
85
|
+
rb_method_entry_t *me;
|
86
|
+
struct unlinked_method_entry_list_entry *ume;
|
87
|
+
};
|
88
|
+
|
89
|
+
#else /* RUBY_192 */
|
90
|
+
|
91
|
+
struct METHOD {
|
92
|
+
VALUE recv;
|
93
|
+
VALUE rclass;
|
94
|
+
ID id;
|
95
|
+
rb_method_entry_t me;
|
96
|
+
struct unlinked_method_entry_list_entry *ume;
|
97
|
+
};
|
98
|
+
|
99
|
+
#endif
|
100
|
+
|
101
|
+
#else /* RUBY18 */
|
102
|
+
|
103
|
+
#include "node.h"
|
104
|
+
|
105
|
+
/* Copy-pasted out of Ruby 1.8.7, not part of the official C API.*/
|
106
|
+
struct METHOD {
|
107
|
+
VALUE klass, rklass;
|
108
|
+
VALUE recv;
|
109
|
+
ID id, oid;
|
110
|
+
int safe_level;
|
111
|
+
NODE *body;
|
112
|
+
};
|
113
|
+
|
114
|
+
#endif
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: c_location
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.2'
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-04-
|
12
|
+
date: 2012-04-09 00:00:00.000000000Z
|
13
13
|
dependencies: []
|
14
14
|
description: Allows you to find the source location of methods written in C
|
15
15
|
email: conrad.irwin@gmail.com
|
@@ -22,6 +22,9 @@ files:
|
|
22
22
|
- lib/c_location/resolver.rb
|
23
23
|
- ext/extconf.rb
|
24
24
|
- ext/c_location.c
|
25
|
+
- ext/c_location.h
|
26
|
+
- example/c_extension_location.rb
|
27
|
+
- example/inbuilt_location.rb
|
25
28
|
- README.md
|
26
29
|
- LICENSE.MIT
|
27
30
|
homepage: http://github.com/ConradIrwin/c_location
|
@@ -50,4 +53,3 @@ signing_key:
|
|
50
53
|
specification_version: 3
|
51
54
|
summary: ! 'Adds a #c_location to Method and UnboundMethod objects.'
|
52
55
|
test_files: []
|
53
|
-
has_rdoc:
|