Skip to content

Commit b7e2acf

Browse files
committed
pprof encoder
1 parent 1826dea commit b7e2acf

File tree

13 files changed

+314
-45
lines changed

13 files changed

+314
-45
lines changed

pyroscope_ffi/ruby/ext/rbspy/src/lib.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use pyroscope_rbspy::{rbspy_backend, RbspyConfig};
1010

1111
use pyroscope::{pyroscope::Compression, PyroscopeAgent};
1212
use pyroscope::backend::{Report, StackFrame, Tag};
13+
use pyroscope::pyroscope::ReportEncoding;
1314

1415
pub fn transform_report(report: Report) -> Report {
1516
let cwd = env::current_dir().unwrap();
@@ -107,7 +108,8 @@ pub extern "C" fn initialize_logging(logging_level: u32) -> bool {
107108
pub extern "C" fn initialize_agent(
108109
application_name: *const c_char, server_address: *const c_char, auth_token: *const c_char,
109110
sample_rate: u32, detect_subprocesses: bool, oncpu: bool, report_pid: bool,
110-
report_thread_id: bool, tags: *const c_char, compression: *const c_char
111+
report_thread_id: bool, tags: *const c_char, compression: *const c_char,
112+
report_encoding: *const c_char
111113
) -> bool {
112114
// Initialize FFIKit
113115
let recv = ffikit::initialize_ffi().unwrap();
@@ -137,7 +139,14 @@ pub extern "C" fn initialize_agent(
137139
.unwrap()
138140
.to_string();
139141

142+
let report_encoding = unsafe { CStr::from_ptr(report_encoding) }
143+
.to_str()
144+
.unwrap()
145+
.to_string();
146+
140147
let compression = Compression::from_str(&compression_string);
148+
let report_encoding = ReportEncoding::from_str(&report_encoding)
149+
.unwrap_or(ReportEncoding::PPROF);
141150

142151
let pid = std::process::id();
143152

@@ -156,7 +165,8 @@ pub extern "C" fn initialize_agent(
156165
let mut agent_builder = PyroscopeAgent::builder(server_address, application_name)
157166
.backend(rbspy)
158167
.func(transform_report)
159-
.tags(tags);
168+
.tags(tags)
169+
.report_encoding(report_encoding);
160170

161171
if auth_token != "" {
162172
agent_builder = agent_builder.auth_token(auth_token);

pyroscope_ffi/ruby/lib/pyroscope.rb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ module Rust
88
extend FFI::Library
99
ffi_lib File.expand_path(File.dirname(__FILE__)) + "/rbspy/rbspy.#{RbConfig::CONFIG["DLEXT"]}"
1010
attach_function :initialize_logging, [:int], :bool
11-
attach_function :initialize_agent, [:string, :string, :string, :int, :bool, :bool, :bool, :bool, :string, :string], :bool
11+
attach_function :initialize_agent, [:string, :string, :string, :int, :bool, :bool, :bool, :bool, :string, :string, :string], :bool
1212
attach_function :add_thread_tag, [:uint64, :string, :string], :bool
1313
attach_function :remove_thread_tag, [:uint64, :string, :string], :bool
1414
attach_function :add_global_tag, [:string, :string], :bool
@@ -22,7 +22,7 @@ module Utils
2222
attach_function :thread_id, [], :uint64
2323
end
2424

25-
Config = Struct.new(:application_name, :app_name, :server_address, :auth_token, :log_level, :sample_rate, :detect_subprocesses, :oncpu, :report_pid, :report_thread_id, :tags, :compression) do
25+
Config = Struct.new(:application_name, :app_name, :server_address, :auth_token, :log_level, :sample_rate, :detect_subprocesses, :oncpu, :report_pid, :report_thread_id, :tags, :compression, :report_encoding) do
2626
def initialize(*)
2727
super
2828
# defaults:
@@ -37,6 +37,7 @@ def initialize(*)
3737
self.log_level = 'error'
3838
self.tags = {}
3939
self.compression = 'gzip'
40+
self.report_encoding = 'pprof'
4041
end
4142
end
4243

@@ -79,7 +80,8 @@ def configure
7980
@config.report_pid || false,
8081
@config.report_thread_id || false,
8182
tags_to_string(@config.tags || {}),
82-
@config.compression || ""
83+
@config.compression || "",
84+
@config.report_encoding || "pprof"
8385
)
8486
end
8587

src/backend/types.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,15 @@ pub struct Report {
130130
pub metadata: Metadata,
131131
}
132132

133+
#[derive(Debug)]
134+
pub struct EncodedReport {
135+
pub format: String,
136+
pub content_type: String,
137+
pub content_encoding: String,
138+
pub data: Vec<u8>,
139+
pub metadata: Metadata,
140+
}
141+
133142
/// Custom implementation of the Hash trait for Report.
134143
/// Only the metadata is hashed.
135144
impl Hash for Report {

src/encode/folded.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
2+
use crate::backend;
3+
4+
use backend::types::{Report, EncodedReport};
5+
6+
pub fn encode(reports: Vec<Report>) -> Vec<EncodedReport> {
7+
reports.into_iter()
8+
.map(|r| {
9+
EncodedReport {
10+
format: "folded".to_string(),
11+
content_type: "binary/octet-stream".to_string(),
12+
content_encoding: "".to_string(),
13+
data: r.to_string().into_bytes(),
14+
metadata: r.metadata,
15+
}
16+
}).collect()
17+
}

src/encode/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pub mod folded;
2+
pub mod pprof;
3+
pub mod profiles;
4+
5+

src/encode/pprof.rs

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
use std::collections::HashMap;
2+
3+
use prost::Message;
4+
5+
use crate::backend::types::{EncodedReport, Report};
6+
use crate::encode::profiles::{Function, Line, Location, Profile, Sample, ValueType};
7+
8+
9+
struct PProfBuilder {
10+
profile: Profile,
11+
strings: HashMap<String, i64>,
12+
functions: HashMap<i64, u64>,
13+
locations: HashMap<u64, u64>,
14+
}
15+
16+
impl PProfBuilder {
17+
fn add_string(&mut self, s: &String) -> i64 {
18+
let v = self.strings.get(s);
19+
if v.is_some() {
20+
return *v.unwrap();
21+
}
22+
assert!(self.strings.len() != self.profile.string_table.len() + 1);
23+
let id: i64 = self.strings.len() as i64;
24+
self.strings.insert(s.to_owned(), id);
25+
self.profile.string_table.push(s.to_owned());
26+
id
27+
}
28+
29+
fn add_function(&mut self, name: i64) -> u64 {
30+
let v = self.functions.get(&name);
31+
if v.is_some() {
32+
return *v.unwrap();
33+
}
34+
assert!(self.functions.len() != self.profile.function.len() + 1);
35+
let id: u64 = self.functions.len() as u64 + 1;
36+
let f = Function {
37+
id: id,
38+
name: name,
39+
system_name: 0,
40+
filename: 0,
41+
start_line: 0,
42+
};
43+
self.functions.insert(name, id);
44+
self.profile.function.push(f);
45+
id
46+
}
47+
48+
fn add_location(&mut self, function_id: u64) -> u64 {
49+
let v = self.locations.get(&function_id);
50+
if v.is_some() {
51+
return *v.unwrap();
52+
}
53+
assert!(self.locations.len() != self.profile.location.len() + 1);
54+
let id: u64 = self.locations.len() as u64 + 1;
55+
let l = Location {
56+
id,
57+
mapping_id: 0,
58+
address: 0,
59+
line: vec![Line {
60+
function_id: function_id,
61+
line: 0,
62+
}],
63+
is_folded: false,
64+
};
65+
self.locations.insert(function_id, id);
66+
self.profile.location.push(l);
67+
id
68+
}
69+
}
70+
71+
pub fn encode(reports: Vec<Report>) -> Vec<EncodedReport> {
72+
let mut b = PProfBuilder {
73+
strings: HashMap::new(),
74+
functions: HashMap::new(),
75+
locations: HashMap::new(),
76+
profile: Profile {
77+
sample_type: vec![],
78+
sample: vec![],
79+
mapping: vec![],
80+
location: vec![],
81+
function: vec![],
82+
string_table: vec![],
83+
drop_frames: 0,
84+
keep_frames: 0,
85+
time_nanos: 0,
86+
duration_nanos: 0,
87+
period_type: None,
88+
period: 0,
89+
comment: vec![],
90+
default_sample_type: 0,
91+
},
92+
};
93+
{
94+
let cpu = b.add_string(&"cpu".to_string());
95+
let samples = b.add_string(&"samples".to_string());
96+
b.profile.sample_type.push(ValueType {
97+
r#type: samples,
98+
unit: cpu,
99+
});
100+
}
101+
for report in &reports {
102+
for (stacktrace, value) in &report.data {
103+
let mut sample = Sample {
104+
location_id: vec![],
105+
value: vec![*value as i64],
106+
label: vec![],
107+
};
108+
for sf in &stacktrace.frames {
109+
let name = format!("{}:{} - {}",
110+
sf.filename.as_ref().unwrap_or(&"".to_string()),
111+
sf.line.unwrap_or(0),
112+
sf.name.as_ref().unwrap_or(&"".to_string()));
113+
let name = b.add_string(&name);
114+
let function_id = b.add_function(name);
115+
let location_id = b.add_location(function_id);
116+
sample.location_id.push(location_id as u64);
117+
}
118+
b.profile.sample.push(sample);
119+
}
120+
}
121+
122+
vec![EncodedReport {
123+
format: "pprof".to_string(),
124+
content_type: "binary/octet-stream".to_string(),
125+
content_encoding: "".to_string(),
126+
data: b.profile.encode_to_vec(),
127+
metadata: Default::default(),
128+
}]
129+
}

src/pprof/pprof.proto renamed to src/encode/profiles.proto

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
syntax = "proto3";
4040

4141
//package perftools.profiles;
42-
package pprof;
42+
package profiles;
4343

4444
option java_package = "com.google.perftools.profiles";
4545
option java_outer_classname = "ProfileProto";
File renamed without changes.

src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
//! agent_ready.shutdown();
4040
//! ```
4141
42+
extern crate core;
43+
4244
// Re-exports structs
4345
pub use crate::pyroscope::PyroscopeAgent;
4446
pub use error::{PyroscopeError, Result};
@@ -52,4 +54,4 @@ pub mod timer;
5254

5355
// Private modules
5456
mod utils;
55-
mod pprof;
57+
mod encode;

src/pprof/mod.rs

Lines changed: 0 additions & 2 deletions
This file was deleted.

0 commit comments

Comments
 (0)