Skip to content

Commit 9328ae1

Browse files
committed
feat: skip recursive frames
1 parent 348f659 commit 9328ae1

File tree

5 files changed

+67
-21
lines changed

5 files changed

+67
-21
lines changed

.ocamlformat

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
profile = conventional
2+
3+
# Preserve begin/end or parentheses to group expressions. Discussed November
4+
# 2024 with mixed opinions. Some prefer the consistency of always using
5+
# parentheses. Others find it easier to visually parse large blocks when
6+
# delimited by begin/end. For now we'll relax the constraints and let people
7+
# experiment. We can easily move back to enforcing parens only if we want to.
8+
exp-grouping = preserve
9+
10+
# Docstrings are supposed to be structured and parsable. We don't use them
11+
# properly, which makes ocamlformat 0.27.0 sad. Disable docstring parsing until
12+
# we get it together.
13+
parse-docstrings = false

dune-project

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
(source
88
(github semgrep/pyro-caml))
99

10-
; with ocaml linking exception
10+
; with ocaml linking exception?
1111
(license LGPL-2.1-only)
1212

1313
(authors "Austin Theriault <[email protected]>")
@@ -17,7 +17,7 @@
1717
(package
1818
(name pyro-caml-instruments)
1919
(synopsis "Pyroscope + OCaml = Pyro Caml")
20-
(depends ocaml logs digestif)
20+
(depends ocaml logs digestif ppx_deriving)
2121
(tags ("profiling")))
2222

2323
(package

lib/Stack_trace.ml

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@
2020
type slot = Printexc.backtrace_slot
2121

2222
(* What's sent via runtime events. this HAS to be marshalable*)
23-
type raw_stack_trace = {slots: slot array; domain_id: int; thread_name: string}
23+
type raw_stack_trace = {
24+
slots : slot array;
25+
domain_id : int;
26+
thread_name : string;
27+
}
2428

2529
let raw_stack_trace_of_backtrace bt : raw_stack_trace =
2630
(* Use the domain as the ID since runtime event sampling happens per domain *)
@@ -30,7 +34,7 @@ let raw_stack_trace_of_backtrace bt : raw_stack_trace =
3034
let name = if Domain.is_main_domain () then "main" else string_of_int did in
3135
(* if there aren't any slots then not much we can do *)
3236
let slots = Option.value ~default:[||] Printexc.(backtrace_slots bt) in
33-
{slots; domain_id= did; thread_name= name}
37+
{ slots; domain_id = did; thread_name = name }
3438

3539
(*****************************************************************************)
3640
(* Stack frames *)
@@ -39,39 +43,65 @@ let raw_stack_trace_of_backtrace bt : raw_stack_trace =
3943
(* Inlined functions are filtered out in ocaml_intf always right now, but we
4044
probably want to give that as an option at some point *)
4145
(* coupling: ocaml_intf *)
42-
type frame = {name: string; filename: string; line: int; inlined: bool}
46+
type frame = {
47+
name : string;
48+
filename : string;
49+
line : int;
50+
inlined : bool; [@eq.skip]
51+
(* We really don't care about if a function is inlined for equality*)
52+
}
53+
[@@deriving eq]
4354

4455
let stack_frame_of_slot (slot : Printexc.backtrace_slot) : frame option =
4556
let loc = Printexc.Slot.location slot in
4657
let name = Printexc.Slot.name slot in
4758
let inlined = Printexc.Slot.is_inline slot in
4859
match (loc, name) with
4960
| Some loc, Some name ->
50-
Some {name; filename= loc.filename; line= loc.line_number; inlined}
51-
| None, Some name ->
52-
Some {name; filename= "<unknown>"; line= 0; inlined}
61+
Some { name; filename = loc.filename; line = loc.line_number; inlined }
62+
| None, Some name -> Some { name; filename = "<unknown>"; line = 0; inlined }
5363
| Some loc, None ->
5464
Some
55-
{ name= "<unknown>"
56-
; filename= loc.filename
57-
; line= loc.line_number
58-
; inlined }
59-
| None, None ->
60-
None
65+
{
66+
name = "<unknown>";
67+
filename = loc.filename;
68+
line = loc.line_number;
69+
inlined;
70+
}
71+
| None, None -> None
72+
73+
(* Looking ahead by up to 3 can be useful for recursive functions to make them
74+
much more legible. E.g. List.map can be very recursive, and pyroscope has a
75+
~1000 stack frame limit, so if we iterate through say, 10k items there's a
76+
good chance we may max out, which will cause any frames past the limit to be
77+
dropped and instead replaced with a single frame that says "other"*)
78+
let compress frames =
79+
let rec aux acc = function
80+
| [] -> List.rev acc
81+
| f1 :: f2 :: f3 :: (f4 :: f5 :: f6 :: _ as rest)
82+
when equal_frame f1 f4 && equal_frame f2 f5 && equal_frame f3 f6 ->
83+
aux acc rest
84+
| f1 :: f2 :: (f3 :: f4 :: _ as rest)
85+
when equal_frame f1 f3 && equal_frame f2 f4 ->
86+
aux acc rest
87+
| f1 :: (f2 :: _ as rest) when equal_frame f1 f2 -> aux acc rest
88+
| f :: rest -> aux (f :: acc) rest
89+
in
90+
aux [] frames
6191

6292
let stack_frames_of_slots slots =
63-
slots
64-
|> List.map (fun slot -> slot |> stack_frame_of_slot)
65-
|> List.filter_map Fun.id
93+
slots |> List.filter_map stack_frame_of_slot |> compress
6694

6795
(*****************************************************************************)
6896
(* Stack traces *)
6997
(*****************************************************************************)
7098
(* coupling: ocaml_intf *)
71-
type t = {frames: frame list; thread_id: int; thread_name: string}
99+
type t = { frames : frame list; thread_id : int; thread_name : string }
72100

73101
let t_of_raw_stack_trace raw_stack_trace =
74102
let frames = stack_frames_of_slots (Array.to_list raw_stack_trace.slots) in
75-
{ frames
76-
; thread_id= raw_stack_trace.domain_id
77-
; thread_name= raw_stack_trace.thread_name }
103+
{
104+
frames;
105+
thread_id = raw_stack_trace.domain_id;
106+
thread_name = raw_stack_trace.thread_name;
107+
}

lib/dune

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,7 @@
22
(name pyro_caml_instruments)
33
(public_name pyro-caml-instruments)
44
(libraries runtime_events unix logs digestif)
5+
(preprocess
6+
(pps ppx_deriving.eq))
57
(instrumentation.backend
68
(ppx pyro-caml-ppx)))

pyro-caml-instruments.opam

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ depends: [
1212
"ocaml"
1313
"logs"
1414
"digestif"
15+
"ppx_deriving"
1516
"odoc" {with-doc}
1617
]
1718
build: [

0 commit comments

Comments
 (0)