1
1
use crate :: bindings:: { zai_str_from_zstr, zend_execute_data, zend_function} ;
2
- use std:: borrow:: Cow ;
2
+ use crate :: well_known:: WellKnown ;
3
+ use datadog_alloc:: Global ;
4
+ use datadog_thin_str:: ThinString ;
5
+ use std:: ops:: Deref ;
3
6
use std:: str:: Utf8Error ;
4
7
5
8
#[ cfg( php_frameless) ]
6
9
use crate :: bindings:: zend_flf_functions;
10
+
7
11
#[ cfg( php_frameless) ]
8
12
use crate :: bindings:: {
9
13
ZEND_FRAMELESS_ICALL_0 , ZEND_FRAMELESS_ICALL_1 , ZEND_FRAMELESS_ICALL_2 , ZEND_FRAMELESS_ICALL_3 ,
10
14
} ;
11
15
12
- const COW_PHP_OPEN_TAG : Cow < str > = Cow :: Borrowed ( "<?php" ) ;
13
- const COW_TRUNCATED : Cow < str > = Cow :: Borrowed ( "[truncated]" ) ;
14
-
15
16
#[ derive( Default , Debug ) ]
16
17
pub struct ZendFrame {
17
18
// Most tools don't like frames that don't have function names, so use a
18
19
// fake name if you need to like "<?php".
19
- pub function : Cow < ' static , str > ,
20
- pub file : Option < String > ,
20
+ pub function : ThinString ,
21
+ pub file : Option < ThinString > ,
21
22
pub line : u32 , // use 0 for no line info
22
23
}
23
24
@@ -30,7 +31,7 @@ pub struct ZendFrame {
30
31
/// Namespaces are part of the class_name or function_name respectively.
31
32
/// Closures and anonymous classes get reformatted by the backend (or maybe
32
33
/// frontend, either way it's not our concern, at least not right now).
33
- pub fn extract_function_name ( func : & zend_function ) -> Option < String > {
34
+ pub fn extract_function_name ( func : & zend_function ) -> Option < ThinString > {
34
35
let method_name: & [ u8 ] = func. name ( ) . unwrap_or ( b"" ) ;
35
36
36
37
/* The top of the stack seems to reasonably often not have a function, but
@@ -59,15 +60,17 @@ pub fn extract_function_name(func: &zend_function) -> Option<String> {
59
60
60
61
buffer. extend_from_slice ( method_name) ;
61
62
62
- Some ( String :: from_utf8_lossy ( buffer. as_slice ( ) ) . into_owned ( ) )
63
+ let lossy = String :: from_utf8_lossy ( buffer. as_slice ( ) ) ;
64
+ Some ( ThinString :: from_str_in ( & lossy, Global ) )
63
65
}
64
66
65
- unsafe fn extract_file_and_line ( execute_data : & zend_execute_data ) -> ( Option < String > , u32 ) {
67
+ unsafe fn extract_file_and_line ( execute_data : & zend_execute_data ) -> ( Option < ThinString > , u32 ) {
66
68
// This should be Some, just being cautious.
67
69
match execute_data. func . as_ref ( ) {
68
70
Some ( func) if !func. is_internal ( ) => {
69
71
// Safety: zai_str_from_zstr will return a valid ZaiStr.
70
- let file = zai_str_from_zstr ( func. op_array . filename . as_mut ( ) ) . into_string ( ) ;
72
+ let file_lossy = zai_str_from_zstr ( func. op_array . filename . as_mut ( ) ) . into_string_lossy ( ) ;
73
+ let file = ThinString :: from_str_in ( file_lossy. deref ( ) , Global ) ;
71
74
let lineno = match execute_data. opline . as_ref ( ) {
72
75
Some ( opline) => opline. lineno ,
73
76
None => 0 ,
@@ -82,9 +85,10 @@ unsafe fn extract_file_and_line(execute_data: &zend_execute_data) -> (Option<Str
82
85
mod detail {
83
86
use super :: * ;
84
87
use crate :: string_set:: StringSet ;
85
- use crate :: thin_str :: ThinStr ;
88
+ use datadog_thin_str :: ThinStr ;
86
89
use log:: { debug, trace} ;
87
90
use std:: cell:: RefCell ;
91
+ use std:: ops:: Deref ;
88
92
use std:: ptr:: NonNull ;
89
93
90
94
struct StringCache < ' a > {
@@ -100,9 +104,9 @@ mod detail {
100
104
/// string in the slot currently, then create one by calling the
101
105
/// provided function, store it in the string cache and cache slot,
102
106
/// and return it.
103
- fn get_or_insert < F > ( & mut self , slot : usize , f : F ) -> Option < String >
107
+ fn get_or_insert < F > ( & mut self , slot : usize , f : F ) -> Option < ThinString >
104
108
where
105
- F : FnOnce ( ) -> Option < String > ,
109
+ F : FnOnce ( ) -> Option < ThinString > ,
106
110
{
107
111
debug_assert ! ( slot < self . cache_slots. len( ) ) ;
108
112
let cached = unsafe { self . cache_slots . get_unchecked_mut ( slot) } ;
@@ -116,7 +120,7 @@ mod detail {
116
120
// so this ThinStr points into the same string set that
117
121
// created it.
118
122
let str = unsafe { self . string_set . get_thin_str ( thin_str) } ;
119
- Some ( str . to_string ( ) )
123
+ Some ( ThinString :: from_str_in ( str , Global ) )
120
124
}
121
125
None => {
122
126
let string = f ( ) ?;
@@ -221,7 +225,7 @@ mod detail {
221
225
& * * zend_flf_functions. offset ( opline. extended_value as isize )
222
226
} ;
223
227
samples. push ( ZendFrame {
224
- function : extract_function_name ( func) . map ( Cow :: Owned ) . unwrap ( ) ,
228
+ function : extract_function_name ( func) . unwrap ( ) ,
225
229
file : None ,
226
230
line : 0 ,
227
231
} ) ;
@@ -240,7 +244,7 @@ mod detail {
240
244
*/
241
245
if samples. len ( ) == max_depth - 1 {
242
246
samples. push ( ZendFrame {
243
- function : COW_TRUNCATED ,
247
+ function : ThinString :: from ( WellKnown :: Truncated ) ,
244
248
file : None ,
245
249
line : 0 ,
246
250
} ) ;
@@ -292,15 +296,15 @@ mod detail {
292
296
let mut stats = cell. borrow_mut ( ) ;
293
297
stats. not_applicable += 1 ;
294
298
} ) ;
295
- let function = extract_function_name ( func) . map ( Cow :: Owned ) ;
299
+ let function = extract_function_name ( func) ;
296
300
let ( file, line) = extract_file_and_line ( execute_data) ;
297
301
( function, file, line)
298
302
}
299
303
} ;
300
304
301
305
if function. is_some ( ) || file. is_some ( ) {
302
306
Some ( ZendFrame {
303
- function : function. unwrap_or ( COW_PHP_OPEN_TAG ) ,
307
+ function : function. unwrap_or ( WellKnown :: PhpOpenTag . into ( ) ) ,
304
308
file,
305
309
line,
306
310
} )
@@ -312,16 +316,16 @@ mod detail {
312
316
fn handle_function_cache_slot (
313
317
func : & zend_function ,
314
318
string_cache : & mut StringCache ,
315
- ) -> Option < Cow < ' static , str > > {
319
+ ) -> Option < ThinString > {
316
320
let fname = string_cache. get_or_insert ( 0 , || extract_function_name ( func) ) ?;
317
- Some ( Cow :: Owned ( fname) )
321
+ Some ( ThinString :: from_str_in ( & fname, Global ) )
318
322
}
319
323
320
324
unsafe fn handle_file_cache_slot (
321
325
execute_data : & zend_execute_data ,
322
326
string_cache : & mut StringCache ,
323
- ) -> ( Option < String > , u32 ) {
324
- let option = string_cache. get_or_insert ( 1 , || -> Option < String > {
327
+ ) -> ( Option < ThinString > , u32 ) {
328
+ let option = string_cache. get_or_insert ( 1 , || -> Option < ThinString > {
325
329
unsafe {
326
330
// Safety: if we have cache slots, we definitely have a func.
327
331
let func = & * execute_data. func ;
@@ -330,7 +334,9 @@ mod detail {
330
334
} ;
331
335
332
336
// SAFETY: calling C function with correct args.
333
- let file = zai_str_from_zstr ( func. op_array . filename . as_mut ( ) ) . into_string ( ) ;
337
+ let file_lossy =
338
+ zai_str_from_zstr ( func. op_array . filename . as_mut ( ) ) . into_string_lossy ( ) ;
339
+ let file = ThinString :: from_str_in ( file_lossy. deref ( ) , Global ) ;
334
340
Some ( file)
335
341
}
336
342
} ) ;
@@ -341,7 +347,7 @@ mod detail {
341
347
Some ( opline) => opline. lineno ,
342
348
None => 0 ,
343
349
} ;
344
- ( Some ( filename) , lineno)
350
+ ( Some ( ThinString :: from_str_in ( & filename, Global ) ) , lineno)
345
351
}
346
352
None => ( None , 0 ) ,
347
353
}
@@ -380,7 +386,7 @@ mod detail {
380
386
*/
381
387
if samples. len ( ) == max_depth - 1 {
382
388
samples. push ( ZendFrame {
383
- function : COW_TRUNCATED ,
389
+ function : WellKnown :: Truncated . into ( ) ,
384
390
file : None ,
385
391
line : 0 ,
386
392
} ) ;
@@ -401,7 +407,7 @@ mod detail {
401
407
// Only create a new frame if there's file or function info.
402
408
if file. is_some ( ) || function. is_some ( ) {
403
409
// If there's no function name, use a fake name.
404
- let function = function. map ( Cow :: Owned ) . unwrap_or ( COW_PHP_OPEN_TAG ) ;
410
+ let function = function. unwrap_or ( WellKnown :: PhpOpenTag . into ( ) ) ;
405
411
return Some ( ZendFrame {
406
412
function,
407
413
file,
@@ -415,6 +421,17 @@ mod detail {
415
421
416
422
pub use detail:: * ;
417
423
424
+ #[ cfg( test) ]
425
+ mod size_tests {
426
+ use super :: * ;
427
+ use core:: mem:: size_of;
428
+
429
+ #[ test]
430
+ fn test_frame_size ( ) {
431
+ assert_eq ! ( size_of:: <ZendFrame >( ) , size_of:: <usize >( ) * 3 ) ;
432
+ }
433
+ }
434
+
418
435
#[ cfg( all( test, stack_walking_tests) ) ]
419
436
mod tests {
420
437
use super :: * ;
0 commit comments