@@ -18,9 +18,23 @@ impl InternalProfile {
18
18
/// earlier time. The duration will be set to zero these cases.
19
19
pub fn serialize_into_otel (
20
20
mut self ,
21
- _end_time : Option < std:: time:: SystemTime > ,
22
- _duration : Option < std:: time:: Duration > ,
21
+ end_time : Option < std:: time:: SystemTime > ,
22
+ duration : Option < std:: time:: Duration > ,
23
23
) -> anyhow:: Result < datadog_profiling_otel:: ProfilesData > {
24
+ // Calculate duration using the same logic as encode
25
+ let end = end_time. unwrap_or_else ( std:: time:: SystemTime :: now) ;
26
+ let start = self . start_time ;
27
+ let duration_nanos = duration
28
+ . unwrap_or_else ( || {
29
+ end. duration_since ( start) . unwrap_or ( {
30
+ // Let's not throw away the whole profile just because the clocks were wrong.
31
+ // todo: log that the clock went backward (or programmer mistake).
32
+ std:: time:: Duration :: ZERO
33
+ } )
34
+ } )
35
+ . as_nanos ( )
36
+ . min ( i64:: MAX as u128 ) as i64 ;
37
+
24
38
// Create individual OpenTelemetry Profiles for each ValueType
25
39
let mut profiles = Vec :: with_capacity ( self . sample_types . len ( ) ) ;
26
40
@@ -42,7 +56,7 @@ impl InternalProfile {
42
56
. duration_since ( std:: time:: UNIX_EPOCH )
43
57
. unwrap_or_default ( )
44
58
. as_nanos ( ) as i64 ,
45
- duration_nanos : 0 , // TODO: Calculate from internal profile
59
+ duration_nanos, // Use calculated duration
46
60
period_type : None , // TODO: Implement when we handle period types
47
61
period : 0 , // TODO: Implement when we handle periods
48
62
comment_strindices : vec ! [ ] , // TODO: Implement when we handle comments
@@ -202,6 +216,15 @@ mod tests {
202
216
assert_eq ! ( dictionary. attribute_units. len( ) , 0 ) ;
203
217
204
218
assert_eq ! ( otel_profiles_data. resource_profiles. len( ) , 1 ) ;
219
+
220
+ // Check duration calculation - only if profiles exist
221
+ let scope_profile = & otel_profiles_data. resource_profiles [ 0 ] . scope_profiles [ 0 ] ;
222
+ if !scope_profile. profiles . is_empty ( ) {
223
+ let profile = & scope_profile. profiles [ 0 ] ;
224
+ // When no duration is provided, it should calculate from current time - start time
225
+ // Since we're testing with None, None, the duration should be > 0 (current time - start time)
226
+ assert ! ( profile. duration_nanos > 0 ) ;
227
+ }
205
228
}
206
229
207
230
#[ test]
@@ -245,6 +268,14 @@ mod tests {
245
268
assert_eq ! ( otel_function2. name_strindex, 7 ) ;
246
269
assert_eq ! ( otel_function2. system_name_strindex, 8 ) ;
247
270
assert_eq ! ( otel_function2. filename_strindex, 9 ) ;
271
+
272
+ // Check duration calculation - only if profiles exist
273
+ let scope_profile = & otel_profiles_data. resource_profiles [ 0 ] . scope_profiles [ 0 ] ;
274
+ if !scope_profile. profiles . is_empty ( ) {
275
+ let profile = & scope_profile. profiles [ 0 ] ;
276
+ // When no duration is provided, it should calculate from current time - start time
277
+ assert ! ( profile. duration_nanos > 0 ) ;
278
+ }
248
279
}
249
280
250
281
#[ test]
@@ -314,6 +345,14 @@ mod tests {
314
345
// and the unit should map to the "bytes" string index
315
346
assert ! ( unit. attribute_key_strindex > 0 ) ;
316
347
assert ! ( unit. unit_strindex > 0 ) ;
348
+
349
+ // Check duration calculation - only if profiles exist
350
+ let scope_profile = & otel_profiles_data. resource_profiles [ 0 ] . scope_profiles [ 0 ] ;
351
+ if !scope_profile. profiles . is_empty ( ) {
352
+ let profile = & scope_profile. profiles [ 0 ] ;
353
+ // When no duration is provided, it should calculate from current time - start time
354
+ assert ! ( profile. duration_nanos > 0 ) ;
355
+ }
317
356
}
318
357
319
358
#[ test]
@@ -350,6 +389,12 @@ mod tests {
350
389
let allocations_sample_type = allocations_profile. sample_type . as_ref ( ) . unwrap ( ) ;
351
390
assert_eq ! ( allocations_sample_type. type_strindex, 6 ) ; // "allocations" string index
352
391
assert_eq ! ( allocations_sample_type. unit_strindex, 7 ) ; // "count" string index
392
+
393
+ // Check duration calculation for both profiles
394
+ for profile in & scope_profile. profiles {
395
+ // When no duration is provided, it should calculate from current time - start time
396
+ assert ! ( profile. duration_nanos > 0 ) ;
397
+ }
353
398
}
354
399
355
400
#[ test]
@@ -429,6 +474,12 @@ mod tests {
429
474
assert_eq ! ( memory_sample. values, vec![ 2048 ] ) ;
430
475
assert_eq ! ( memory_sample. stack_index, 0 ) ; // First stack trace
431
476
assert_eq ! ( memory_sample. attribute_indices. len( ) , 0 ) ; // No labels
477
+
478
+ // Check duration calculation for both profiles
479
+ for profile in & otel_profiles_data. resource_profiles [ 0 ] . scope_profiles [ 0 ] . profiles {
480
+ // When no duration is provided, it should calculate from current time - start time
481
+ assert ! ( profile. duration_nanos > 0 ) ;
482
+ }
432
483
}
433
484
434
485
#[ test]
@@ -505,6 +556,10 @@ mod tests {
505
556
assert_eq ! ( sample. attribute_indices. len( ) , 2 ) ;
506
557
// The attribute indices should correspond to the labels in the attribute table
507
558
assert ! ( sample. attribute_indices[ 0 ] >= 0 ) ;
559
+
560
+ // Check duration calculation
561
+ // When no duration is provided, it should calculate from current time - start time
562
+ assert ! ( profile. duration_nanos > 0 ) ;
508
563
assert ! ( sample. attribute_indices[ 1 ] >= 0 ) ;
509
564
510
565
// Verify the attributes were converted correctly
@@ -568,6 +623,10 @@ mod tests {
568
623
// Verify the sample has the correct timestamp
569
624
assert_eq ! ( sample. timestamps_unix_nano. len( ) , 1 ) ;
570
625
assert_eq ! ( sample. timestamps_unix_nano[ 0 ] , 1234567890 ) ;
626
+
627
+ // Check duration calculation
628
+ // When no duration is provided, it should calculate from current time - start time
629
+ assert ! ( profile. duration_nanos > 0 ) ;
571
630
}
572
631
573
632
#[ test]
@@ -629,6 +688,12 @@ mod tests {
629
688
assert_eq ! ( profile1. sample. len( ) , 1 ) ;
630
689
let memory_sample = & profile1. sample [ 0 ] ;
631
690
assert_eq ! ( memory_sample. values, vec![ 1024 ] ) ;
691
+
692
+ // Check duration calculation for both profiles
693
+ for profile in & otel_profiles_data. resource_profiles [ 0 ] . scope_profiles [ 0 ] . profiles {
694
+ // When no duration is provided, it should calculate from current time - start time
695
+ assert ! ( profile. duration_nanos > 0 ) ;
696
+ }
632
697
}
633
698
634
699
#[ test]
@@ -703,5 +768,45 @@ mod tests {
703
768
for sample in & profile. sample {
704
769
assert_eq ! ( sample. stack_index, 0 ) ;
705
770
}
771
+
772
+ // Check duration calculation
773
+ // When no duration is provided, it should calculate from current time - start time
774
+ assert ! ( profile. duration_nanos > 0 ) ;
775
+ }
776
+
777
+ #[ test]
778
+ fn test_duration_calculation ( ) {
779
+ // Create an internal profile with sample types
780
+ let sample_types = [ crate :: api:: ValueType :: new ( "cpu" , "nanoseconds" ) ] ;
781
+ let internal_profile = InternalProfile :: new ( & sample_types, None ) ;
782
+
783
+ // Test with explicit duration
784
+ let explicit_duration = std:: time:: Duration :: from_secs ( 5 ) ;
785
+ let otel_profiles_data = internal_profile. serialize_into_otel ( None , Some ( explicit_duration) ) . unwrap ( ) ;
786
+
787
+ let profile = & otel_profiles_data. resource_profiles [ 0 ] . scope_profiles [ 0 ] . profiles [ 0 ] ;
788
+ // Should use the explicit duration (5 seconds = 5_000_000_000 nanoseconds)
789
+ assert_eq ! ( profile. duration_nanos, 5_000_000_000 ) ;
790
+
791
+ // Test with explicit end_time
792
+ let internal_profile2 = InternalProfile :: new ( & sample_types, None ) ;
793
+ let start_time = internal_profile2. start_time ;
794
+ let end_time = start_time + std:: time:: Duration :: from_secs ( 3 ) ;
795
+ let otel_profiles_data2 = internal_profile2. serialize_into_otel ( Some ( end_time) , None ) . unwrap ( ) ;
796
+
797
+ let profile2 = & otel_profiles_data2. resource_profiles [ 0 ] . scope_profiles [ 0 ] . profiles [ 0 ] ;
798
+ // Should calculate duration from end_time - start_time (3 seconds = 3_000_000_000 nanoseconds)
799
+ assert_eq ! ( profile2. duration_nanos, 3_000_000_000 ) ;
800
+
801
+ // Test with both end_time and duration (duration should take precedence)
802
+ let internal_profile3 = InternalProfile :: new ( & sample_types, None ) ;
803
+ let start_time3 = internal_profile3. start_time ;
804
+ let end_time3 = start_time3 + std:: time:: Duration :: from_secs ( 10 ) ;
805
+ let duration3 = std:: time:: Duration :: from_secs ( 7 ) ;
806
+ let otel_profiles_data3 = internal_profile3. serialize_into_otel ( Some ( end_time3) , Some ( duration3) ) . unwrap ( ) ;
807
+
808
+ let profile3 = & otel_profiles_data3. resource_profiles [ 0 ] . scope_profiles [ 0 ] . profiles [ 0 ] ;
809
+ // Should use the explicit duration (7 seconds = 7_000_000_000 nanoseconds)
810
+ assert_eq ! ( profile3. duration_nanos, 7_000_000_000 ) ;
706
811
}
707
812
}
0 commit comments