@@ -82,19 +82,35 @@ impl FunctionLocations for VecFunctionLocations {
82
82
}
83
83
}
84
84
85
- pub type LineNumber = u16 ; // TODO u32, newtype
85
+ /// Either the line number, or the bytecode index needed to get it.
86
+ #[ derive( Copy , Clone , Serialize , Deserialize , Hash , Eq , PartialEq , Debug ) ]
87
+ pub enum LineNumberInfo {
88
+ LineNumber ( u32 ) ,
89
+ BytecodeIndex ( i32 ) ,
90
+ }
91
+
92
+ impl LineNumberInfo {
93
+ fn get_line_number ( & self ) -> u32 {
94
+ if let LineNumberInfo :: LineNumber ( lineno) = self {
95
+ * lineno
96
+ } else {
97
+ debug_assert ! ( false , "This should never happen." ) ;
98
+ 0
99
+ }
100
+ }
101
+ }
86
102
87
103
/// A specific location: file + function + line number.
88
104
#[ derive( Clone , Debug , PartialEq , Eq , Copy , Hash , Serialize , Deserialize ) ]
89
105
pub struct CallSiteId {
90
106
/// The function + filename. We use IDs for performance reasons (faster hashing).
91
- function : FunctionId ,
107
+ pub function : FunctionId ,
92
108
/// Line number within the _file_, 1-indexed.
93
- line_number : LineNumber ,
109
+ pub line_number : LineNumberInfo ,
94
110
}
95
111
96
112
impl CallSiteId {
97
- pub fn new ( function : FunctionId , line_number : u16 ) -> CallSiteId {
113
+ pub fn new ( function : FunctionId , line_number : LineNumberInfo ) -> CallSiteId {
98
114
CallSiteId {
99
115
function,
100
116
line_number,
@@ -108,7 +124,7 @@ impl CallSiteId {
108
124
pub struct Callstack {
109
125
calls : Vec < CallSiteId > ,
110
126
#[ derivative( Hash = "ignore" , PartialEq = "ignore" ) ]
111
- cached_callstack_id : Option < ( u16 , CallstackId ) > , // first bit is line number
127
+ cached_callstack_id : Option < ( u32 , CallstackId ) > , // first bit is line number
112
128
}
113
129
114
130
impl Callstack {
@@ -126,10 +142,14 @@ impl Callstack {
126
142
}
127
143
}
128
144
129
- pub fn start_call ( & mut self , parent_line_number : u16 , callsite_id : CallSiteId ) {
145
+ pub fn to_vec ( & self ) -> Vec < CallSiteId > {
146
+ self . calls . clone ( )
147
+ }
148
+
149
+ pub fn start_call ( & mut self , parent_line_number : u32 , callsite_id : CallSiteId ) {
130
150
if parent_line_number != 0 {
131
151
if let Some ( mut call) = self . calls . last_mut ( ) {
132
- call. line_number = parent_line_number;
152
+ call. line_number = LineNumberInfo :: LineNumber ( parent_line_number) ;
133
153
}
134
154
}
135
155
self . calls . push ( callsite_id) ;
@@ -141,7 +161,7 @@ impl Callstack {
141
161
self . cached_callstack_id = None ;
142
162
}
143
163
144
- pub fn id_for_new_allocation < F > ( & mut self , line_number : u16 , get_callstack_id : F ) -> CallstackId
164
+ pub fn id_for_new_allocation < F > ( & mut self , line_number : u32 , get_callstack_id : F ) -> CallstackId
145
165
where
146
166
F : FnOnce ( & Callstack ) -> CallstackId ,
147
167
{
@@ -156,7 +176,7 @@ impl Callstack {
156
176
// Set the new line number:
157
177
if line_number != 0 {
158
178
if let Some ( call) = self . calls . last_mut ( ) {
159
- call. line_number = line_number;
179
+ call. line_number = LineNumberInfo :: LineNumber ( line_number) ;
160
180
}
161
181
}
162
182
@@ -194,7 +214,8 @@ impl Callstack {
194
214
. map ( |( id, ( function, filename) ) | {
195
215
if to_be_post_processed {
196
216
// Get Python code.
197
- let code = linecache. get_source_line ( filename, id. line_number as usize ) ;
217
+ let code = linecache
218
+ . get_source_line ( filename, id. line_number . get_line_number ( ) as usize ) ;
198
219
// Leading whitespace is dropped by SVG, so we'd like to
199
220
// replace it with non-breaking space. However, inferno
200
221
// trims whitespace
@@ -215,15 +236,15 @@ impl Callstack {
215
236
format ! (
216
237
"{filename}:{line} ({function});\u{2800} {code}" ,
217
238
filename = filename,
218
- line = id. line_number,
239
+ line = id. line_number. get_line_number ( ) ,
219
240
function = function,
220
241
code = & code. trim_end( ) ,
221
242
)
222
243
} else {
223
244
format ! (
224
245
"{filename}:{line} ({function})" ,
225
246
filename = filename,
226
- line = id. line_number,
247
+ line = id. line_number. get_line_number ( ) ,
227
248
function = function,
228
249
)
229
250
}
@@ -335,6 +356,10 @@ impl Allocation {
335
356
}
336
357
}
337
358
359
+ fn same_callstack ( callstack : & Callstack ) -> Cow < Callstack > {
360
+ Cow :: Borrowed ( callstack)
361
+ }
362
+
338
363
/// The main data structure tracking everything.
339
364
pub struct AllocationTracker < FL : FunctionLocations > {
340
365
// malloc()/calloc():
@@ -607,18 +632,22 @@ impl<FL: FunctionLocations> AllocationTracker<FL> {
607
632
self . dump_to_flamegraph ( path, true , "peak-memory" , "Peak Tracked Memory Usage" , true ) ;
608
633
}
609
634
610
- pub fn to_lines (
611
- & self ,
635
+ pub fn to_lines < ' a , F > (
636
+ & ' a self ,
612
637
peak : bool ,
613
638
to_be_post_processed : bool ,
614
- ) -> impl ExactSizeIterator < Item = String > + ' _ {
639
+ update_callstack : F ,
640
+ ) -> impl ExactSizeIterator < Item = String > + ' _
641
+ where
642
+ F : Fn ( & Callstack ) -> Cow < Callstack > + ' a ,
643
+ {
615
644
let by_call = self . combine_callstacks ( peak) . into_iter ( ) ;
616
645
let id_to_callstack = self . interner . get_reverse_map ( ) ;
617
646
let mut linecache = LineCacher :: default ( ) ;
618
647
by_call. map ( move |( callstack_id, size) | {
619
648
format ! (
620
649
"{} {}" ,
621
- id_to_callstack. get( & callstack_id) . unwrap( ) . as_string(
650
+ update_callstack ( id_to_callstack. get( & callstack_id) . unwrap( ) ) . as_string(
622
651
to_be_post_processed,
623
652
& self . functions,
624
653
";" ,
@@ -675,7 +704,7 @@ impl<FL: FunctionLocations> AllocationTracker<FL> {
675
704
subtitle,
676
705
"bytes" ,
677
706
to_be_post_processed,
678
- |tbpp| self . to_lines ( peak, tbpp) ,
707
+ |tbpp| self . to_lines ( peak, tbpp, same_callstack ) ,
679
708
)
680
709
}
681
710
@@ -746,8 +775,9 @@ impl<FL: FunctionLocations> AllocationTracker<FL> {
746
775
747
776
#[ cfg( test) ]
748
777
mod tests {
749
- use crate :: memorytracking:: { ProcessUid , PARENT_PROCESS } ;
778
+ use crate :: memorytracking:: { same_callstack , ProcessUid , PARENT_PROCESS } ;
750
779
780
+ use super :: LineNumberInfo :: LineNumber ;
751
781
use super :: {
752
782
Allocation , AllocationTracker , CallSiteId , Callstack , CallstackInterner , FunctionId ,
753
783
FunctionLocations , VecFunctionLocations , HIGH_32BIT , MIB ,
@@ -815,7 +845,7 @@ mod tests {
815
845
let ( process, allocation_size) = * allocated_sizes. get( i) . unwrap( ) ;
816
846
let process = ProcessUid ( process) ;
817
847
let mut cs = Callstack :: new( ) ;
818
- cs. start_call( 0 , CallSiteId :: new( FunctionId :: new( i as u64 ) , 0 ) ) ;
848
+ cs. start_call( 0 , CallSiteId :: new( FunctionId :: new( i as u64 ) , LineNumber ( 0 ) ) ) ;
819
849
let cs_id = tracker. get_callstack_id( & cs) ;
820
850
tracker. add_allocation( process, i as usize , allocation_size, cs_id) ;
821
851
expected_memory_usage. push_back( allocation_size) ;
@@ -854,7 +884,7 @@ mod tests {
854
884
let ( process, allocation_size) = * allocated_sizes. get( i) . unwrap( ) ;
855
885
let process = ProcessUid ( process) ;
856
886
let mut cs = Callstack :: new( ) ;
857
- cs. start_call( 0 , CallSiteId :: new( FunctionId :: new( i as u64 ) , 0 ) ) ;
887
+ cs. start_call( 0 , CallSiteId :: new( FunctionId :: new( i as u64 ) , LineNumber ( 0 ) ) ) ;
858
888
let csid = tracker. get_callstack_id( & cs) ;
859
889
tracker. add_anon_mmap( process, addresses[ i] as usize , allocation_size, csid) ;
860
890
expected_memory_usage. push_back( allocation_size) ;
@@ -891,7 +921,7 @@ mod tests {
891
921
let ( process, allocation_size) = * allocated_sizes. get( i) . unwrap( ) ;
892
922
let process = ProcessUid ( process) ;
893
923
let mut cs = Callstack :: new( ) ;
894
- cs. start_call( 0 , CallSiteId :: new( FunctionId :: new( i as u64 ) , 0 ) ) ;
924
+ cs. start_call( 0 , CallSiteId :: new( FunctionId :: new( i as u64 ) , LineNumber ( 0 ) ) ) ;
895
925
let cs_id = tracker. get_callstack_id( & cs) ;
896
926
tracker. add_allocation( process, i as usize , allocation_size, cs_id) ;
897
927
expected_memory_usage += allocation_size;
@@ -900,7 +930,7 @@ mod tests {
900
930
let ( process, allocation_size) = * allocated_mmaps. get( i) . unwrap( ) ;
901
931
let process = ProcessUid ( process) ;
902
932
let mut cs = Callstack :: new( ) ;
903
- cs. start_call( 0 , CallSiteId :: new( FunctionId :: new( i as u64 ) , 0 ) ) ;
933
+ cs. start_call( 0 , CallSiteId :: new( FunctionId :: new( i as u64 ) , LineNumber ( 0 ) ) ) ;
904
934
let csid = tracker. get_callstack_id( & cs) ;
905
935
tracker. add_anon_mmap( process, mmap_addresses[ i] as usize , allocation_size, csid) ;
906
936
expected_memory_usage += allocation_size;
@@ -933,9 +963,10 @@ mod tests {
933
963
934
964
// Parent line number does nothing if it's first call:
935
965
let mut cs1 = Callstack :: new ( ) ;
936
- let id1 = CallSiteId :: new ( fid1, 2 ) ;
937
- let id2 = CallSiteId :: new ( fid3, 45 ) ;
938
- let id3 = CallSiteId :: new ( fid5, 6 ) ;
966
+ // CallSiteId::new($a, $b) ==>> CallSiteId::new($a, LineNumber($b))
967
+ let id1 = CallSiteId :: new ( fid1, LineNumber ( 2 ) ) ;
968
+ let id2 = CallSiteId :: new ( fid3, LineNumber ( 45 ) ) ;
969
+ let id3 = CallSiteId :: new ( fid5, LineNumber ( 6 ) ) ;
939
970
cs1. start_call ( 123 , id1) ;
940
971
assert_eq ! ( cs1. calls, vec![ id1] ) ;
941
972
@@ -950,7 +981,11 @@ mod tests {
950
981
cs2. start_call ( 12 , id3) ;
951
982
assert_eq ! (
952
983
cs2. calls,
953
- vec![ CallSiteId :: new( fid1, 10 ) , CallSiteId :: new( fid3, 12 ) , id3]
984
+ vec![
985
+ CallSiteId :: new( fid1, LineNumber ( 10 ) ) ,
986
+ CallSiteId :: new( fid3, LineNumber ( 12 ) ) ,
987
+ id3
988
+ ]
954
989
) ;
955
990
}
956
991
@@ -960,10 +995,10 @@ mod tests {
960
995
let fid3 = FunctionId :: new ( 3u64 ) ;
961
996
962
997
let mut cs1 = Callstack :: new ( ) ;
963
- cs1. start_call ( 0 , CallSiteId :: new ( fid1, 2 ) ) ;
998
+ cs1. start_call ( 0 , CallSiteId :: new ( fid1, LineNumber ( 2 ) ) ) ;
964
999
let cs1b = cs1. clone ( ) ;
965
1000
let mut cs2 = Callstack :: new ( ) ;
966
- cs2. start_call ( 0 , CallSiteId :: new ( fid3, 4 ) ) ;
1001
+ cs2. start_call ( 0 , CallSiteId :: new ( fid3, LineNumber ( 4 ) ) ) ;
967
1002
let cs3 = Callstack :: new ( ) ;
968
1003
let cs3b = Callstack :: new ( ) ;
969
1004
@@ -1014,7 +1049,7 @@ mod tests {
1014
1049
1015
1050
let fid1 = FunctionId :: new ( 1u64 ) ;
1016
1051
1017
- cs1. start_call ( 0 , CallSiteId :: new ( fid1, 2 ) ) ;
1052
+ cs1. start_call ( 0 , CallSiteId :: new ( fid1, LineNumber ( 2 ) ) ) ;
1018
1053
let id1 =
1019
1054
cs1. id_for_new_allocation ( 1 , |cs| interner. get_or_insert_id ( Cow :: Borrowed ( & cs) , || ( ) ) ) ;
1020
1055
let id2 =
@@ -1025,7 +1060,7 @@ mod tests {
1025
1060
assert_ne ! ( id2, id0) ;
1026
1061
assert_ne ! ( id2, id1) ;
1027
1062
1028
- cs1. start_call ( 3 , CallSiteId :: new ( fid1, 2 ) ) ;
1063
+ cs1. start_call ( 3 , CallSiteId :: new ( fid1, LineNumber ( 2 ) ) ) ;
1029
1064
let id3 =
1030
1065
cs1. id_for_new_allocation ( 4 , |cs| interner. get_or_insert_id ( Cow :: Borrowed ( & cs) , || ( ) ) ) ;
1031
1066
assert_ne ! ( id3, id0) ;
@@ -1041,7 +1076,7 @@ mod tests {
1041
1076
assert_eq ! ( id1, id1c) ;
1042
1077
1043
1078
// Check for cache invalidation in start_call:
1044
- cs1. start_call ( 1 , CallSiteId :: new ( fid1, 1 ) ) ;
1079
+ cs1. start_call ( 1 , CallSiteId :: new ( fid1, LineNumber ( 1 ) ) ) ;
1045
1080
let id4 =
1046
1081
cs1. id_for_new_allocation ( 1 , |cs| interner. get_or_insert_id ( Cow :: Borrowed ( & cs) , || ( ) ) ) ;
1047
1082
assert_ne ! ( id4, id0) ;
@@ -1063,9 +1098,9 @@ mod tests {
1063
1098
1064
1099
let mut tracker = new_tracker ( ) ;
1065
1100
let mut cs1 = Callstack :: new ( ) ;
1066
- cs1. start_call ( 0 , CallSiteId :: new ( fid1, 2 ) ) ;
1101
+ cs1. start_call ( 0 , CallSiteId :: new ( fid1, LineNumber ( 2 ) ) ) ;
1067
1102
let mut cs2 = Callstack :: new ( ) ;
1068
- cs2. start_call ( 0 , CallSiteId :: new ( fid3, 4 ) ) ;
1103
+ cs2. start_call ( 0 , CallSiteId :: new ( fid3, LineNumber ( 4 ) ) ) ;
1069
1104
1070
1105
let cs1_id = tracker. get_callstack_id ( & cs1) ;
1071
1106
@@ -1156,12 +1191,12 @@ mod tests {
1156
1191
. functions
1157
1192
. add_function ( "c" . to_string ( ) , "cf" . to_string ( ) ) ;
1158
1193
1159
- let id1 = CallSiteId :: new ( fid1, 1 ) ;
1194
+ let id1 = CallSiteId :: new ( fid1, LineNumber ( 1 ) ) ;
1160
1195
// Same function, different line number—should be different item:
1161
- let id1_different = CallSiteId :: new ( fid1, 7 ) ;
1162
- let id2 = CallSiteId :: new ( fid2, 2 ) ;
1196
+ let id1_different = CallSiteId :: new ( fid1, LineNumber ( 7 ) ) ;
1197
+ let id2 = CallSiteId :: new ( fid2, LineNumber ( 2 ) ) ;
1163
1198
1164
- let id3 = CallSiteId :: new ( fid3, 3 ) ;
1199
+ let id3 = CallSiteId :: new ( fid3, LineNumber ( 3 ) ) ;
1165
1200
let mut cs1 = Callstack :: new ( ) ;
1166
1201
cs1. start_call ( 0 , id1) ;
1167
1202
cs1. start_call ( 0 , id2. clone ( ) ) ;
@@ -1200,7 +1235,7 @@ mod tests {
1200
1235
"c:3 (cf) 234" ,
1201
1236
"a:7 (af);b:2 (bf) 6000" ,
1202
1237
] ;
1203
- let mut result2: Vec < String > = tracker. to_lines ( true , false ) . collect ( ) ;
1238
+ let mut result2: Vec < String > = tracker. to_lines ( true , false , same_callstack ) . collect ( ) ;
1204
1239
result2. sort ( ) ;
1205
1240
expected2. sort ( ) ;
1206
1241
assert_eq ! ( expected2, result2) ;
0 commit comments