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 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.
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'c_location'
5
+
6
+ puts CompiledLocation.instance_method(:compiled_location).c_location.inspect
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'c_location'
5
+
6
+ puts Math.method(:sin).c_location.inspect
data/ext/c_location.c CHANGED
@@ -1,15 +1,4 @@
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;
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.1'
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-08 00:00:00.000000000Z
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: