pyroscope-ruby33 0.5.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +23 -0
- data/LICENSE +202 -0
- data/README.md +71 -0
- data/ext/rbspy/Cargo.toml +21 -0
- data/ext/rbspy/Rakefile +164 -0
- data/ext/rbspy/build.rs +12 -0
- data/ext/rbspy/cbindgen.toml +22 -0
- data/ext/rbspy/extconf.rb +11 -0
- data/ext/rbspy/include/rbspy.h +43 -0
- data/ext/rbspy/src/lib.rs +358 -0
- data/ext/thread_id/Cargo.toml +15 -0
- data/ext/thread_id/Rakefile +163 -0
- data/ext/thread_id/build.rs +12 -0
- data/ext/thread_id/cbindgen.toml +22 -0
- data/ext/thread_id/extconf.rb +11 -0
- data/ext/thread_id/include/thread_id.h +17 -0
- data/ext/thread_id/src/lib.rs +4 -0
- data/lib/pyroscope/version.rb +3 -0
- data/lib/pyroscope.rb +192 -0
- data/pyroscope.gemspec +71 -0
- metadata +113 -0
@@ -0,0 +1,358 @@
|
|
1
|
+
use std::collections::hash_map::DefaultHasher;
|
2
|
+
use std::env;
|
3
|
+
use std::ffi::CStr;
|
4
|
+
use std::hash::Hasher;
|
5
|
+
use std::os::raw::c_char;
|
6
|
+
use std::str::FromStr;
|
7
|
+
|
8
|
+
use ffikit::Signal;
|
9
|
+
use pyroscope_rbspy::{rbspy_backend, RbspyConfig};
|
10
|
+
|
11
|
+
use pyroscope;
|
12
|
+
use pyroscope::{pyroscope::Compression, PyroscopeAgent};
|
13
|
+
use pyroscope::backend::{Report, StackFrame, Tag};
|
14
|
+
use pyroscope::pyroscope::ReportEncoding;
|
15
|
+
|
16
|
+
const LOG_TAG: &str = "Pyroscope::rbspy::ffi";
|
17
|
+
|
18
|
+
|
19
|
+
pub fn transform_report(report: Report) -> Report {
|
20
|
+
let cwd = env::current_dir().unwrap();
|
21
|
+
let cwd = cwd.to_str().unwrap_or("");
|
22
|
+
|
23
|
+
let data = report
|
24
|
+
.data
|
25
|
+
.iter()
|
26
|
+
.map(|(stacktrace, count)| {
|
27
|
+
let new_frames = stacktrace
|
28
|
+
.frames
|
29
|
+
.iter()
|
30
|
+
.map(|frame| {
|
31
|
+
let frame = frame.to_owned();
|
32
|
+
let mut s = frame.filename.unwrap();
|
33
|
+
match s.find(cwd) {
|
34
|
+
Some(i) => {
|
35
|
+
s = s[(i + cwd.len() + 1)..].to_string();
|
36
|
+
}
|
37
|
+
None => match s.find("/gems/") {
|
38
|
+
Some(i) => {
|
39
|
+
s = s[(i + 1)..].to_string();
|
40
|
+
}
|
41
|
+
None => match s.find("/ruby/") {
|
42
|
+
Some(i) => {
|
43
|
+
s = s[(i + 6)..].to_string();
|
44
|
+
match s.find("/") {
|
45
|
+
Some(i) => {
|
46
|
+
s = s[(i + 1)..].to_string();
|
47
|
+
}
|
48
|
+
None => {}
|
49
|
+
}
|
50
|
+
}
|
51
|
+
None => {}
|
52
|
+
},
|
53
|
+
},
|
54
|
+
}
|
55
|
+
|
56
|
+
// something
|
57
|
+
StackFrame::new(
|
58
|
+
frame.module,
|
59
|
+
frame.name,
|
60
|
+
Some(s.to_string()),
|
61
|
+
frame.relative_path,
|
62
|
+
frame.absolute_path,
|
63
|
+
frame.line,
|
64
|
+
)
|
65
|
+
})
|
66
|
+
.collect();
|
67
|
+
|
68
|
+
let mut mystack = stacktrace.to_owned();
|
69
|
+
|
70
|
+
mystack.frames = new_frames;
|
71
|
+
|
72
|
+
(mystack, count.to_owned())
|
73
|
+
})
|
74
|
+
.collect();
|
75
|
+
|
76
|
+
let new_report = Report::new(data).metadata(report.metadata.clone());
|
77
|
+
|
78
|
+
new_report
|
79
|
+
}
|
80
|
+
|
81
|
+
#[no_mangle]
|
82
|
+
pub extern "C" fn initialize_logging(logging_level: u32) -> bool {
|
83
|
+
// Force rustc to display the log messages in the console.
|
84
|
+
match logging_level {
|
85
|
+
50 => {
|
86
|
+
std::env::set_var("RUST_LOG", "error");
|
87
|
+
}
|
88
|
+
40 => {
|
89
|
+
std::env::set_var("RUST_LOG", "warn");
|
90
|
+
}
|
91
|
+
30 => {
|
92
|
+
std::env::set_var("RUST_LOG", "info");
|
93
|
+
}
|
94
|
+
20 => {
|
95
|
+
std::env::set_var("RUST_LOG", "debug");
|
96
|
+
}
|
97
|
+
10 => {
|
98
|
+
std::env::set_var("RUST_LOG", "trace");
|
99
|
+
}
|
100
|
+
_ => {
|
101
|
+
std::env::set_var("RUST_LOG", "debug");
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
105
|
+
// Initialize the logger.
|
106
|
+
pretty_env_logger::init_timed();
|
107
|
+
|
108
|
+
true
|
109
|
+
}
|
110
|
+
|
111
|
+
#[no_mangle]
|
112
|
+
pub extern "C" fn initialize_agent(
|
113
|
+
application_name: *const c_char,
|
114
|
+
server_address: *const c_char,
|
115
|
+
auth_token: *const c_char,
|
116
|
+
basic_auth_user: *const c_char,
|
117
|
+
basic_auth_password: *const c_char,
|
118
|
+
sample_rate: u32,
|
119
|
+
detect_subprocesses: bool,
|
120
|
+
oncpu: bool,
|
121
|
+
report_pid: bool,
|
122
|
+
report_thread_id: bool,
|
123
|
+
tags: *const c_char,
|
124
|
+
compression: *const c_char,
|
125
|
+
_report_encoding: *const c_char,
|
126
|
+
tenant_id: *const c_char,
|
127
|
+
http_headers_json: *const c_char,
|
128
|
+
) -> bool {
|
129
|
+
// Initialize FFIKit
|
130
|
+
let recv = ffikit::initialize_ffi().unwrap();
|
131
|
+
|
132
|
+
let application_name = unsafe { CStr::from_ptr(application_name) }
|
133
|
+
.to_str()
|
134
|
+
.unwrap()
|
135
|
+
.to_string();
|
136
|
+
|
137
|
+
let mut server_address = unsafe { CStr::from_ptr(server_address) }
|
138
|
+
.to_str()
|
139
|
+
.unwrap()
|
140
|
+
.to_string();
|
141
|
+
|
142
|
+
let adhoc_server_address = std::env::var("PYROSCOPE_ADHOC_SERVER_ADDRESS");
|
143
|
+
if let Ok(adhoc_server_address) = adhoc_server_address {
|
144
|
+
server_address = adhoc_server_address
|
145
|
+
}
|
146
|
+
|
147
|
+
let auth_token = unsafe { CStr::from_ptr(auth_token) }
|
148
|
+
.to_str()
|
149
|
+
.unwrap()
|
150
|
+
.to_string();
|
151
|
+
|
152
|
+
let basic_auth_user = unsafe { CStr::from_ptr(basic_auth_user) }
|
153
|
+
.to_str()
|
154
|
+
.unwrap()
|
155
|
+
.to_string();
|
156
|
+
|
157
|
+
let basic_auth_password = unsafe { CStr::from_ptr(basic_auth_password) }
|
158
|
+
.to_str()
|
159
|
+
.unwrap()
|
160
|
+
.to_string();
|
161
|
+
|
162
|
+
let tags_string = unsafe { CStr::from_ptr(tags) }
|
163
|
+
.to_str()
|
164
|
+
.unwrap()
|
165
|
+
.to_string();
|
166
|
+
|
167
|
+
let compression_string = unsafe { CStr::from_ptr(compression) }
|
168
|
+
.to_str()
|
169
|
+
.unwrap()
|
170
|
+
.to_string();
|
171
|
+
|
172
|
+
let tenant_id = unsafe { CStr::from_ptr(tenant_id) }
|
173
|
+
.to_str()
|
174
|
+
.unwrap()
|
175
|
+
.to_string();
|
176
|
+
|
177
|
+
let http_headers_json = unsafe { CStr::from_ptr(http_headers_json) }
|
178
|
+
.to_str()
|
179
|
+
.unwrap()
|
180
|
+
.to_string();
|
181
|
+
|
182
|
+
let compression = Compression::from_str(&compression_string);
|
183
|
+
|
184
|
+
let pid = std::process::id();
|
185
|
+
|
186
|
+
let rbspy_config = RbspyConfig::new(pid.try_into().unwrap())
|
187
|
+
.sample_rate(sample_rate)
|
188
|
+
.lock_process(false)
|
189
|
+
.detect_subprocesses(detect_subprocesses)
|
190
|
+
.oncpu(oncpu)
|
191
|
+
.report_pid(report_pid)
|
192
|
+
.report_thread_id(report_thread_id);
|
193
|
+
|
194
|
+
let tags_ref = tags_string.as_str();
|
195
|
+
let tags = string_to_tags(tags_ref);
|
196
|
+
let rbspy = rbspy_backend(rbspy_config);
|
197
|
+
|
198
|
+
let mut agent_builder = PyroscopeAgent::builder(server_address, application_name)
|
199
|
+
.backend(rbspy)
|
200
|
+
.func(transform_report)
|
201
|
+
.tags(tags)
|
202
|
+
.report_encoding(ReportEncoding::PPROF);
|
203
|
+
|
204
|
+
if auth_token != "" {
|
205
|
+
agent_builder = agent_builder.auth_token(auth_token);
|
206
|
+
} else if basic_auth_user != "" && basic_auth_password != "" {
|
207
|
+
agent_builder = agent_builder.basic_auth(basic_auth_user, basic_auth_password);
|
208
|
+
}
|
209
|
+
|
210
|
+
if tenant_id != "" {
|
211
|
+
agent_builder = agent_builder.tenant_id(tenant_id);
|
212
|
+
}
|
213
|
+
|
214
|
+
let http_headers = pyroscope::pyroscope::parse_http_headers_json(http_headers_json);
|
215
|
+
match http_headers {
|
216
|
+
Ok(http_headers) => {
|
217
|
+
agent_builder = agent_builder.http_headers(http_headers);
|
218
|
+
}
|
219
|
+
Err(e) => {
|
220
|
+
match e {
|
221
|
+
pyroscope::PyroscopeError::Json(e) => {
|
222
|
+
log::error!(target: LOG_TAG, "parse_http_headers_json error {}", e);
|
223
|
+
}
|
224
|
+
pyroscope::PyroscopeError::AdHoc(e) => {
|
225
|
+
log::error!(target: LOG_TAG, "parse_http_headers_json {}", e);
|
226
|
+
}
|
227
|
+
_ => {}
|
228
|
+
}
|
229
|
+
}
|
230
|
+
}
|
231
|
+
|
232
|
+
if let Ok(compression) = compression {
|
233
|
+
agent_builder = agent_builder.compression(compression);
|
234
|
+
}
|
235
|
+
|
236
|
+
let agent = agent_builder.build().unwrap();
|
237
|
+
|
238
|
+
let agent_running = agent.start().unwrap();
|
239
|
+
|
240
|
+
std::thread::spawn(move || {
|
241
|
+
while let Ok(signal) = recv.recv() {
|
242
|
+
match signal {
|
243
|
+
Signal::Kill => {
|
244
|
+
agent_running.stop().unwrap();
|
245
|
+
break;
|
246
|
+
}
|
247
|
+
Signal::AddGlobalTag(name, value) => {
|
248
|
+
agent_running.add_global_tag(Tag::new(name, value)).unwrap();
|
249
|
+
}
|
250
|
+
Signal::RemoveGlobalTag(name, value) => {
|
251
|
+
agent_running
|
252
|
+
.remove_global_tag(Tag::new(name, value))
|
253
|
+
.unwrap();
|
254
|
+
}
|
255
|
+
Signal::AddThreadTag(thread_id, key, value) => {
|
256
|
+
let tag = Tag::new(key, value);
|
257
|
+
agent_running.add_thread_tag(thread_id, tag).unwrap();
|
258
|
+
}
|
259
|
+
Signal::RemoveThreadTag(thread_id, key, value) => {
|
260
|
+
let tag = Tag::new(key, value);
|
261
|
+
agent_running.remove_thread_tag(thread_id, tag).unwrap();
|
262
|
+
}
|
263
|
+
}
|
264
|
+
}
|
265
|
+
});
|
266
|
+
|
267
|
+
true
|
268
|
+
}
|
269
|
+
|
270
|
+
#[no_mangle]
|
271
|
+
pub extern "C" fn drop_agent() -> bool {
|
272
|
+
// Send Kill signal to the FFI merge channel.
|
273
|
+
ffikit::send(ffikit::Signal::Kill).unwrap();
|
274
|
+
|
275
|
+
true
|
276
|
+
}
|
277
|
+
|
278
|
+
#[no_mangle]
|
279
|
+
pub extern "C" fn add_thread_tag(thread_id: u64, key: *const c_char, value: *const c_char) -> bool {
|
280
|
+
let key = unsafe { CStr::from_ptr(key) }.to_str().unwrap().to_owned();
|
281
|
+
let value = unsafe { CStr::from_ptr(value) }
|
282
|
+
.to_str()
|
283
|
+
.unwrap()
|
284
|
+
.to_owned();
|
285
|
+
|
286
|
+
let pid = std::process::id();
|
287
|
+
let mut hasher = DefaultHasher::new();
|
288
|
+
hasher.write_u64(thread_id % pid as u64);
|
289
|
+
let id = hasher.finish();
|
290
|
+
|
291
|
+
ffikit::send(ffikit::Signal::AddThreadTag(id, key, value)).unwrap();
|
292
|
+
|
293
|
+
true
|
294
|
+
}
|
295
|
+
|
296
|
+
#[no_mangle]
|
297
|
+
pub extern "C" fn remove_thread_tag(
|
298
|
+
thread_id: u64, key: *const c_char, value: *const c_char,
|
299
|
+
) -> bool {
|
300
|
+
let key = unsafe { CStr::from_ptr(key) }.to_str().unwrap().to_owned();
|
301
|
+
let value = unsafe { CStr::from_ptr(value) }
|
302
|
+
.to_str()
|
303
|
+
.unwrap()
|
304
|
+
.to_owned();
|
305
|
+
|
306
|
+
let pid = std::process::id();
|
307
|
+
let mut hasher = DefaultHasher::new();
|
308
|
+
hasher.write_u64(thread_id % pid as u64);
|
309
|
+
let id = hasher.finish();
|
310
|
+
|
311
|
+
ffikit::send(ffikit::Signal::RemoveThreadTag(id, key, value)).unwrap();
|
312
|
+
|
313
|
+
true
|
314
|
+
}
|
315
|
+
|
316
|
+
#[no_mangle]
|
317
|
+
pub extern "C" fn add_global_tag(key: *const c_char, value: *const c_char) -> bool {
|
318
|
+
let key = unsafe { CStr::from_ptr(key) }.to_str().unwrap().to_owned();
|
319
|
+
let value = unsafe { CStr::from_ptr(value) }
|
320
|
+
.to_str()
|
321
|
+
.unwrap()
|
322
|
+
.to_owned();
|
323
|
+
|
324
|
+
ffikit::send(ffikit::Signal::AddGlobalTag(key, value)).unwrap();
|
325
|
+
|
326
|
+
true
|
327
|
+
}
|
328
|
+
|
329
|
+
#[no_mangle]
|
330
|
+
pub extern "C" fn remove_global_tag(key: *const c_char, value: *const c_char) -> bool {
|
331
|
+
let key = unsafe { CStr::from_ptr(key) }.to_str().unwrap().to_owned();
|
332
|
+
let value = unsafe { CStr::from_ptr(value) }
|
333
|
+
.to_str()
|
334
|
+
.unwrap()
|
335
|
+
.to_owned();
|
336
|
+
|
337
|
+
ffikit::send(ffikit::Signal::RemoveGlobalTag(key, value)).unwrap();
|
338
|
+
|
339
|
+
true
|
340
|
+
}
|
341
|
+
|
342
|
+
// Convert a string of tags to a Vec<(&str, &str)>
|
343
|
+
fn string_to_tags<'a>(tags: &'a str) -> Vec<(&'a str, &'a str)> {
|
344
|
+
let mut tags_vec = Vec::new();
|
345
|
+
// check if string is empty
|
346
|
+
if tags.is_empty() {
|
347
|
+
return tags_vec;
|
348
|
+
}
|
349
|
+
|
350
|
+
for tag in tags.split(',') {
|
351
|
+
let mut tag_split = tag.split('=');
|
352
|
+
let key = tag_split.next().unwrap();
|
353
|
+
let value = tag_split.next().unwrap();
|
354
|
+
tags_vec.push((key, value));
|
355
|
+
}
|
356
|
+
|
357
|
+
tags_vec
|
358
|
+
}
|
@@ -0,0 +1,163 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "shellwords"
|
4
|
+
|
5
|
+
class ThreadIdRakeCargoHelper
|
6
|
+
attr_reader :gemname
|
7
|
+
|
8
|
+
def initialize(gemname=File.basename(__dir__))
|
9
|
+
@gemname = gemname
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.command?(name)
|
13
|
+
exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""]
|
14
|
+
ENV["PATH"].split(File::PATH_SEPARATOR).any? do |path|
|
15
|
+
exts.any? do |ext|
|
16
|
+
exe = File.join(path, "#{name}#{ext}")
|
17
|
+
File.executable?(exe) && !File.directory?(exe)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.rust_toolchain
|
23
|
+
# return env variable if set
|
24
|
+
target = ENV["RUST_TARGET"]
|
25
|
+
return target if target
|
26
|
+
|
27
|
+
str = `rustc --version --verbose`
|
28
|
+
info = str.lines.map {|l| l.chomp.split(/:\s+/, 2)}.drop(1).to_h
|
29
|
+
info["host"]
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.cargo_target_dir
|
33
|
+
return @cargo_target_dir if defined? @cargo_target_dir
|
34
|
+
|
35
|
+
str = `cargo metadata --format-version 1 --offline --no-deps --quiet`
|
36
|
+
begin
|
37
|
+
require "json"
|
38
|
+
dir = JSON.parse(str)["target_directory"]
|
39
|
+
rescue LoadError # json is usually part of the stdlib, but just in case
|
40
|
+
/"target_directory"\s*:\s*"(?<dir>[^"]*)"/ =~ str
|
41
|
+
end
|
42
|
+
@cargo_target_dir = dir || "target"
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.flags
|
46
|
+
cc_flags = Shellwords.split(RbConfig.expand(RbConfig::MAKEFILE_CONFIG["CC"].dup))
|
47
|
+
|
48
|
+
["-C", "linker=#{cc_flags.shift}",
|
49
|
+
*cc_flags.flat_map {|a| ["-C", "link-arg=#{a}"] },
|
50
|
+
"-L", "native=#{RbConfig::CONFIG["libdir"]}",
|
51
|
+
*dld_flags,
|
52
|
+
*platform_flags]
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.dld_flags
|
56
|
+
Shellwords.split(RbConfig::CONFIG["DLDFLAGS"]).flat_map do |arg|
|
57
|
+
arg = arg.gsub(/\$\((\w+)\)/) do
|
58
|
+
$1 == "DEFFILE" ? nil : RbConfig::CONFIG[name]
|
59
|
+
end.strip
|
60
|
+
next [] if arg.empty?
|
61
|
+
|
62
|
+
transform_flag(arg)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.platform_flags
|
67
|
+
return unless RbConfig::CONFIG["target_os"] =~ /mingw/i
|
68
|
+
|
69
|
+
[*Shellwords.split(RbConfig::CONFIG["LIBRUBYARG"]).flat_map {|arg| transform_flag(arg)},
|
70
|
+
"-C", "link-arg=-Wl,--dynamicbase",
|
71
|
+
"-C", "link-arg=-Wl,--disable-auto-image-base",
|
72
|
+
"-C", "link-arg=-static-libgcc"]
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.transform_flag(arg)
|
76
|
+
k, v = arg.split(/(?<=..)/, 2)
|
77
|
+
case k
|
78
|
+
when "-L"
|
79
|
+
[k, "native=#{v}"]
|
80
|
+
when "-l"
|
81
|
+
[k, v]
|
82
|
+
when "-F"
|
83
|
+
["-l", "framework=#{v}"]
|
84
|
+
else
|
85
|
+
["-C", "link_arg=#{k}#{v}"]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def install_dir
|
90
|
+
File.expand_path(File.join("..", "..", "lib", gemname), __dir__)
|
91
|
+
end
|
92
|
+
|
93
|
+
def rust_name
|
94
|
+
prefix = "lib" unless Gem.win_platform?
|
95
|
+
suffix = if RbConfig::CONFIG["target_os"] =~ /darwin/i
|
96
|
+
".dylib"
|
97
|
+
elsif Gem.win_platform?
|
98
|
+
".dll"
|
99
|
+
else
|
100
|
+
".so"
|
101
|
+
end
|
102
|
+
"#{prefix}#{gemname}#{suffix}"
|
103
|
+
end
|
104
|
+
|
105
|
+
def ruby_name
|
106
|
+
"#{gemname}.#{RbConfig::CONFIG["DLEXT"]}"
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
task default: [:thread_id_install, :thread_id_clean]
|
112
|
+
task thread_id: [:thread_id_install, :thread_id_clean]
|
113
|
+
|
114
|
+
desc "set dev mode for subsequent task, run like `rake dev install`"
|
115
|
+
task :thread_id_dev do
|
116
|
+
@dev = true
|
117
|
+
end
|
118
|
+
|
119
|
+
desc "build gem native extension and copy to lib"
|
120
|
+
task thread_id_install: [:thread_id_cd, :thread_id_build] do
|
121
|
+
helper = ThreadIdRakeCargoHelper.new
|
122
|
+
profile_dir = @dev ? "debug" : "release"
|
123
|
+
arch_dir = RbspyRakeCargoHelper.rust_toolchain
|
124
|
+
source = File.join(ThreadIdRakeCargoHelper.cargo_target_dir, arch_dir, profile_dir, helper.rust_name)
|
125
|
+
dest = File.join(helper.install_dir, helper.ruby_name)
|
126
|
+
mkdir_p(helper.install_dir)
|
127
|
+
rm(dest) if File.exist?(dest)
|
128
|
+
cp(source, dest)
|
129
|
+
end
|
130
|
+
|
131
|
+
desc "build gem native extension"
|
132
|
+
task thread_id_build: [:thread_id_cargo, :thread_id_cd] do
|
133
|
+
sh "cargo", "rustc", *(["--locked", "--release"] unless @dev), "--target=#{RbspyRakeCargoHelper.rust_toolchain}", "--", *RbspyRakeCargoHelper.flags
|
134
|
+
end
|
135
|
+
|
136
|
+
desc "clean up release build artifacts"
|
137
|
+
task thread_id_clean: [:thread_id_cargo, :thread_id_cd] do
|
138
|
+
sh "cargo clean --release"
|
139
|
+
end
|
140
|
+
|
141
|
+
desc "clean up build artifacts"
|
142
|
+
task thread_id_clobber: [:thread_id_cargo, :thread_id_cd] do
|
143
|
+
sh "cargo clean"
|
144
|
+
end
|
145
|
+
|
146
|
+
desc "check for cargo"
|
147
|
+
task :thread_id_cargo do
|
148
|
+
raise <<-MSG unless ThreadIdRakeCargoHelper.command?("cargo")
|
149
|
+
This gem requires a Rust compiler and the `cargo' build tool to build the
|
150
|
+
gem's native extension. See https://www.rust-lang.org/tools/install for
|
151
|
+
how to install Rust. `cargo' is usually part of the Rust installation.
|
152
|
+
MSG
|
153
|
+
|
154
|
+
raise <<-MSG if Gem.win_platform? && ThreadIdRakeCargoHelper.rust_toolchain !~ /gnu/
|
155
|
+
Found Rust toolchain `#{ThreadIdRakeCargoHelper.rust_toolchain}' but the gem native
|
156
|
+
extension requires the gnu toolchain on Windows.
|
157
|
+
MSG
|
158
|
+
end
|
159
|
+
|
160
|
+
# ensure task is running in the right dir
|
161
|
+
task :thread_id_cd do
|
162
|
+
cd(__dir__) unless __dir__ == pwd
|
163
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
extern crate cbindgen;
|
2
|
+
|
3
|
+
use cbindgen::Config;
|
4
|
+
|
5
|
+
fn main() {
|
6
|
+
let bindings = {
|
7
|
+
let crate_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
|
8
|
+
let config = Config::from_file("cbindgen.toml").unwrap();
|
9
|
+
cbindgen::generate_with_config(&crate_dir, config).unwrap()
|
10
|
+
};
|
11
|
+
bindings.write_to_file("include/thread_id.h");
|
12
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# The language to output bindings in
|
2
|
+
language = "C"
|
3
|
+
documentation_style = "C"
|
4
|
+
|
5
|
+
style = "type"
|
6
|
+
|
7
|
+
# An optional name to use as an include guard
|
8
|
+
include_guard = "RBSPY_H_"
|
9
|
+
# include a comment with the version of cbindgen used to generate the file
|
10
|
+
include_version = true
|
11
|
+
|
12
|
+
# An optional string of text to output at the beginning of the generated file
|
13
|
+
header = "/* Licensed under Apache-2.0 */"
|
14
|
+
autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
|
15
|
+
|
16
|
+
braces = "SameLine"
|
17
|
+
tab_width = 2
|
18
|
+
line_length = 80
|
19
|
+
|
20
|
+
[parse]
|
21
|
+
# Do not parse dependent crates
|
22
|
+
parse_deps = false
|
@@ -0,0 +1,17 @@
|
|
1
|
+
/* Licensed under Apache-2.0 */
|
2
|
+
|
3
|
+
#ifndef RBSPY_H_
|
4
|
+
#define RBSPY_H_
|
5
|
+
|
6
|
+
/* Generated with cbindgen:0.20.0 */
|
7
|
+
|
8
|
+
/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */
|
9
|
+
|
10
|
+
#include <stdarg.h>
|
11
|
+
#include <stdbool.h>
|
12
|
+
#include <stdint.h>
|
13
|
+
#include <stdlib.h>
|
14
|
+
|
15
|
+
uint64_t thread_id(void);
|
16
|
+
|
17
|
+
#endif /* RBSPY_H_ */
|