c_location 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE.MIT +19 -0
- data/README.md +0 -0
- data/ext/c_location.c +202 -0
- data/ext/extconf.rb +10 -0
- data/lib/c_location.rb +13 -0
- data/lib/c_location/resolver.rb +106 -0
- metadata +53 -0
data/LICENSE.MIT
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2012 Conrad Irwin <conrad.irwin@gmail.com>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
File without changes
|
data/ext/c_location.c
ADDED
@@ -0,0 +1,202 @@
|
|
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
|
+
/* Given the pointer to a C function (called "name" for error handling)
|
15
|
+
* return the filename and offset of the compiled byte code for that function.
|
16
|
+
*
|
17
|
+
* This can then be used with a variety of Linux utilities to find more information about
|
18
|
+
* that function. If you're really lucky, and your programs have been compiled with
|
19
|
+
* debugging information (CFLAGS=-g) then this can even give you the original location of
|
20
|
+
* the source code of the method.
|
21
|
+
*/
|
22
|
+
static VALUE file_and_offset(void *func, char *name)
|
23
|
+
{
|
24
|
+
Dl2_info info;
|
25
|
+
VALUE file;
|
26
|
+
VALUE offset;
|
27
|
+
|
28
|
+
if(!dladdr(func, &info)) {
|
29
|
+
rb_raise(rb_eRuntimeError, "could not find %s: %s", name, dlerror());
|
30
|
+
}
|
31
|
+
|
32
|
+
file = rb_str_new2(info.dli_fname);
|
33
|
+
offset = LL2NUM((long long)(func - info.dli_fbase));
|
34
|
+
|
35
|
+
return rb_ary_new3(2, file, offset);
|
36
|
+
}
|
37
|
+
|
38
|
+
static VALUE compiled_location(VALUE self);
|
39
|
+
|
40
|
+
void
|
41
|
+
Init_c_location()
|
42
|
+
{
|
43
|
+
VALUE rb_mCompiledLocation = rb_define_module("CompiledLocation");
|
44
|
+
|
45
|
+
rb_define_method(rb_mCompiledLocation, "compiled_location", compiled_location, 0);
|
46
|
+
|
47
|
+
rb_include_module(rb_cMethod, rb_mCompiledLocation);
|
48
|
+
rb_include_module(rb_cUnboundMethod, rb_mCompiledLocation);
|
49
|
+
}
|
50
|
+
|
51
|
+
#ifdef RUBY_19
|
52
|
+
|
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
|
+
#ifdef RUBY_193
|
116
|
+
|
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
|
+
static VALUE compiled_location(VALUE self)
|
126
|
+
{
|
127
|
+
struct METHOD *data;
|
128
|
+
VALUE name = rb_funcall(self, rb_intern("name"), 0);
|
129
|
+
name = rb_funcall(name, rb_intern("to_s"), 0);
|
130
|
+
|
131
|
+
// TODO: We're not validating that this is definitely a method struct.
|
132
|
+
// It would be nice if we could use TypedData_Get_Struct, but we don't
|
133
|
+
// have access to &method_data_type.
|
134
|
+
data = (struct METHOD *)DATA_PTR(self);
|
135
|
+
|
136
|
+
if (data->me->def->type != VM_METHOD_TYPE_CFUNC) {
|
137
|
+
return Qnil;
|
138
|
+
}
|
139
|
+
|
140
|
+
return file_and_offset(*data->me->def->body.cfunc.func, StringValueCStr(name));
|
141
|
+
}
|
142
|
+
|
143
|
+
#else /* RUBY_192 */
|
144
|
+
|
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
|
+
static VALUE compiled_location(VALUE self)
|
154
|
+
{
|
155
|
+
struct METHOD *data;
|
156
|
+
VALUE name = rb_funcall(self, rb_intern("name"), 0);
|
157
|
+
name = rb_funcall(name, rb_intern("to_s"), 0);
|
158
|
+
|
159
|
+
// TODO: We're not validating that this is definitely a method struct.
|
160
|
+
// It would be nice if we could use TypedData_Get_Struct, but we don't
|
161
|
+
// have access to &method_data_type.
|
162
|
+
data = (struct METHOD *)DATA_PTR(self);
|
163
|
+
|
164
|
+
if (data->me.def->type != VM_METHOD_TYPE_CFUNC) {
|
165
|
+
return Qnil;
|
166
|
+
}
|
167
|
+
|
168
|
+
return file_and_offset(*data->me.def->body.cfunc.func, StringValueCStr(name));
|
169
|
+
}
|
170
|
+
|
171
|
+
|
172
|
+
#endif
|
173
|
+
|
174
|
+
#else /* RUBY18 */
|
175
|
+
|
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
|
+
static VALUE compiled_location(VALUE self)
|
188
|
+
{
|
189
|
+
struct METHOD *data;
|
190
|
+
VALUE name = rb_funcall(self, rb_intern("name"), 0);
|
191
|
+
|
192
|
+
Data_Get_Struct(self, struct METHOD, data);
|
193
|
+
|
194
|
+
if (nd_type(data->body) != NODE_CFUNC) {
|
195
|
+
return Qnil;
|
196
|
+
}
|
197
|
+
|
198
|
+
return file_and_offset(*data->body->nd_cfnc, StringValueCStr(name));
|
199
|
+
}
|
200
|
+
|
201
|
+
|
202
|
+
#endif
|
data/ext/extconf.rb
ADDED
data/lib/c_location.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
require File.expand_path('../c_location/resolver', __FILE__)
|
3
|
+
|
4
|
+
module CompiledLocation
|
5
|
+
def c_location
|
6
|
+
if c = compiled_location
|
7
|
+
CompiledLocation::Resolver.new(*c).source_location
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def compiled_location; end
|
12
|
+
require File.expand_path('../../ext/c_location', __FILE__)
|
13
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module CompiledLocation
|
2
|
+
class Resolver
|
3
|
+
attr_accessor :shared_library, :offset
|
4
|
+
|
5
|
+
def initialize(shared_library, offset)
|
6
|
+
self.shared_library, self.offset = [shared_library, offset]
|
7
|
+
self.shared_library = `which ruby` if self.shared_library == 'ruby'
|
8
|
+
end
|
9
|
+
|
10
|
+
def source_location
|
11
|
+
if RUBY_PLATFORM =~ /darwin/
|
12
|
+
nm(shared_library, offset)
|
13
|
+
else
|
14
|
+
objdump(shared_library, offset)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def objdump(shared_library, offset)
|
19
|
+
command = "#{Shellwords.escape which_objdump} --dwarf=decodedline #{Shellwords.escape shared_library}"
|
20
|
+
output = run(command)
|
21
|
+
|
22
|
+
if m = output.lines.detect{ |line| line =~ /\s+0x#{offset.to_s(16)}$/ }
|
23
|
+
file, line, address = m.split(/\s+/)
|
24
|
+
|
25
|
+
absolutify(file, line.to_i(10), shared_library)
|
26
|
+
else
|
27
|
+
raise "Could not find #{shared_library}@0x#{offset.to_s(16)} using #{command.inspect}." +
|
28
|
+
"This may be because your ruby/extensions are not compiled with -g."
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def nm(shared_library, offset)
|
33
|
+
command = "#{Shellwords.escape which_nm} -pa #{Shellwords.escape shared_library}"
|
34
|
+
output = run(command)
|
35
|
+
|
36
|
+
o_file = nil
|
37
|
+
|
38
|
+
output.lines.each do |line|
|
39
|
+
case line
|
40
|
+
when /OSO (.*\.o\)?)/
|
41
|
+
o_file = $1.sub(%r{(.*)/([^/]*)\((.*)\)}, '\1/\3')
|
42
|
+
when /^[01]*#{offset.to_s(16)}.*FUN (.+)/
|
43
|
+
raise "Your version of nm seems to not output OSO lines." unless o_file
|
44
|
+
return objdump_tt(o_file, $1)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
raise "Could not find #{shared_library}@0x#{offset.to_s(16)} using #{command.inspect}." +
|
49
|
+
"This may be because your ruby/extensions are not compiled with -g."
|
50
|
+
end
|
51
|
+
|
52
|
+
def objdump_tt(shared_library, name)
|
53
|
+
output = run("#{Shellwords.escape which_objdump} -tT #{Shellwords.escape shared_library}")
|
54
|
+
|
55
|
+
if output.lines.detect{ |line| line =~ /^([0-9a-f]+).*\.text\s#{Regexp.escape name}$/ }
|
56
|
+
objdump(shared_library, $1.to_i(16))
|
57
|
+
else
|
58
|
+
raise "Could not find #{shared_library}##{name} using #{command.inspect}."
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def which_objdump
|
63
|
+
objdump = `which gobjdump`.chomp
|
64
|
+
return objdump if $?.success?
|
65
|
+
objdump = `which objdump`.chomp
|
66
|
+
return objdump if $?.success?
|
67
|
+
objdump = `which /usr/local/bin/gobjdump`.chomp
|
68
|
+
return objdump if $?.success?
|
69
|
+
|
70
|
+
raise "You need to have `objdump` installed to use c_location. " +
|
71
|
+
"Try installing the binutils package (apt-get install binutils; brew install binutils)."
|
72
|
+
end
|
73
|
+
|
74
|
+
def which_nm
|
75
|
+
nm = `which nm`.chomp
|
76
|
+
return nm if $?.success?
|
77
|
+
|
78
|
+
raise "You need to have `nm` installed to use c_location. " +
|
79
|
+
"Try installing Xcode from the App Store. (GNU nm from binutils doesn't work unfortunately)"
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
def absolutify(file, line, shared_library)
|
84
|
+
if shared_library =~ %r{(.*/(gems|src)/[^/]*)/}
|
85
|
+
potentials = Dir["#{$1}/**/#{file}"]
|
86
|
+
elsif shared_library =~ %r{(.*/(rubies)/[^/]*)/}
|
87
|
+
potentials = Dir["#{$1.sub('/rubies/', '/src/')}/**/#{file}"]
|
88
|
+
else
|
89
|
+
potentials = Dir["./**/#{file}"]
|
90
|
+
end
|
91
|
+
|
92
|
+
if potentials.empty?
|
93
|
+
raise "Could not find the `#{file}:#{line}` that was used to build `#{shared_library}`." +
|
94
|
+
"If you know where this file is please submit a pull request"
|
95
|
+
else
|
96
|
+
[potentials.first, line]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def run(command)
|
101
|
+
`#{command}`.tap do |output|
|
102
|
+
raise "Failed to run #{command.inspect}: #{output}" unless $?.success?
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
metadata
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: c_location
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Conrad Irwin
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-04-08 00:00:00.000000000Z
|
13
|
+
dependencies: []
|
14
|
+
description: Allows you to find the source location of methods written in C
|
15
|
+
email: conrad.irwin@gmail.com
|
16
|
+
executables: []
|
17
|
+
extensions:
|
18
|
+
- ext/extconf.rb
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- lib/c_location.rb
|
22
|
+
- lib/c_location/resolver.rb
|
23
|
+
- ext/extconf.rb
|
24
|
+
- ext/c_location.c
|
25
|
+
- README.md
|
26
|
+
- LICENSE.MIT
|
27
|
+
homepage: http://github.com/ConradIrwin/c_location
|
28
|
+
licenses:
|
29
|
+
- MIT
|
30
|
+
post_install_message:
|
31
|
+
rdoc_options: []
|
32
|
+
require_paths:
|
33
|
+
- lib
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
35
|
+
none: false
|
36
|
+
requirements:
|
37
|
+
- - ! '>='
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
requirements: []
|
47
|
+
rubyforge_project:
|
48
|
+
rubygems_version: 1.8.17
|
49
|
+
signing_key:
|
50
|
+
specification_version: 3
|
51
|
+
summary: ! 'Adds a #c_location to Method and UnboundMethod objects.'
|
52
|
+
test_files: []
|
53
|
+
has_rdoc:
|