diff --git a/src/bin/flamegraph.rs b/src/bin/flamegraph.rs index 7b3daf17..47ea92de 100644 --- a/src/bin/flamegraph.rs +++ b/src/bin/flamegraph.rs @@ -21,7 +21,7 @@ struct Opt { /// Collapsed perf output files. With no INFILE, or INFILE is -, read STDIN. #[structopt(name = "INFILE", parse(from_os_str))] infiles: Vec, - /// set color palette + /// Set color palette #[structopt( short = "c", long = "colors", @@ -31,19 +31,23 @@ struct Opt { ) )] colors: Palette, - /// set background colors. Gradient choices are yellow (default), blue, green, grey; flat colors use "#rrggbb" + /// Set background colors. Gradient choices are yellow (default), blue, green, grey; flat colors use "#rrggbb" #[structopt(long = "bgcolors")] bgcolors: Option, - /// colors are keyed by function name hash + /// Colors are keyed by function name hash #[structopt(long = "hash")] hash: bool, - /// use consistent palette (palette.map) + /// Use consistent palette (palette.map) #[structopt(long = "cp")] cp: bool, - /// switch differential hues (green<->red) + /// Switch differential hues (green<->red) #[structopt(long = "negate")] negate: bool, + + /// Factor to scale sample counts by + #[structopt(long = "factor", default_value = "1.0")] + factor: f64, } impl Into for Opt { @@ -66,6 +70,7 @@ impl Into for Opt { options.title = "Icicle Graph".to_string(); } options.negate_differentials = self.negate; + options.factor = self.factor; options } } diff --git a/src/flamegraph/merge.rs b/src/flamegraph/merge.rs index 172e13b7..0c9a0e18 100644 --- a/src/flamegraph/merge.rs +++ b/src/flamegraph/merge.rs @@ -7,7 +7,7 @@ pub(super) struct Frame<'a> { pub(super) depth: usize, } -#[derive(Debug, PartialEq, Eq, Hash)] +#[derive(Debug, PartialEq)] pub(super) struct TimedFrame<'a> { pub(super) location: Frame<'a>, pub(super) start_time: usize, @@ -15,7 +15,7 @@ pub(super) struct TimedFrame<'a> { pub(super) delta: Option, } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq)] pub(super) struct FrameTime { pub(super) start_time: usize, pub(super) delta: Option, @@ -114,6 +114,7 @@ where let mut frames = Default::default(); let mut delta = None; let mut delta_max = 1; + let mut stripped_fractional_samples = false; for line in lines { let mut line = line.trim(); if line.is_empty() { @@ -124,17 +125,20 @@ where // Usually there will only be one samples column at the end of a line, // but for differentials there will be two. When there are two we compute the // delta between them and use the second one. - let nsamples = if let Some(samples) = parse_nsamples(&mut line) { - // See if there's also a differential column present - if let Some(original_samples) = parse_nsamples(&mut line) { - delta = Some(samples as isize - original_samples as isize); - delta_max = std::cmp::max(delta.unwrap().abs() as usize, delta_max); - } - samples - } else { - ignored += 1; - continue; - }; + let nsamples = + if let Some(samples) = parse_nsamples(&mut line, &mut stripped_fractional_samples) { + // See if there's also a differential column present + if let Some(original_samples) = + parse_nsamples(&mut line, &mut stripped_fractional_samples) + { + delta = Some(samples as isize - original_samples as isize); + delta_max = std::cmp::max(delta.unwrap().abs() as usize, delta_max); + } + samples + } else { + ignored += 1; + continue; + }; if line.is_empty() { ignored += 1; @@ -180,14 +184,22 @@ where } // Parse and remove the number of samples from the end of a line. -fn parse_nsamples(line: &mut &str) -> Option { +fn parse_nsamples(line: &mut &str, stripped_fractional_samples: &mut bool) -> Option { let samplesi = line.rfind(' ')?; let mut samples = &line[(samplesi + 1)..]; - // strip fractional part (if any); + // Strip fractional part (if any); // foobar 1.klwdjlakdj - // TODO: Properly handle fractional samples (see issue #43) + // + // The Perl version keeps the fractional part but this can be problematic + // because of cumulative floating point errors. Instead we recommend to + // use the --factor option. See https://github.com/brendangregg/FlameGraph/pull/18 if let Some(doti) = samples.find('.') { + // Warn if we're stripping a non-zero fractional part, but only the first time. + if !*stripped_fractional_samples && !samples[doti + 1..].chars().all(|c| c == '0') { + *stripped_fractional_samples = true; + warn!("The input data has fractional sample counts that will be truncated to integers. If you need to retain the extra precision you can scale up the sample data and use the --factor option to scale it back down."); + } samples = &samples[..doti]; } diff --git a/src/flamegraph/mod.rs b/src/flamegraph/mod.rs index 9376aaf9..14382efc 100644 --- a/src/flamegraph/mod.rs +++ b/src/flamegraph/mod.rs @@ -76,12 +76,25 @@ pub struct Options { /// /// [differential]: http://www.brendangregg.com/blog/2014-11-09/differential-flame-graphs.html pub negate_differentials: bool, + + /// Factor to scale sample counts by in the flame graph. + /// + /// This option can be useful if the sample data has fractional sample counts since the fractional + /// parts are stripped off when creating the flame graph. To work around this you can scale up the + /// sample counts to be integers, then scale them back down in the graph with the `factor` option. + /// + /// For example, if you have `23.4` as a sample count you can upscale it to `234`, then set `factor` + /// to `0.1`. + /// + /// Defaults to 1.0. + pub factor: f64, } impl Default for Options { fn default() -> Self { Options { title: "Flame Graph".to_string(), + factor: 1.0, colors: Default::default(), bgcolors: Default::default(), hash: Default::default(), @@ -324,7 +337,13 @@ where }; let rect = Rectangle { x1, y1, x2, y2 }; - let samples = frame.end_time - frame.start_time; + // The rounding here can differ from the Perl version when the fractional part is `0.5`. + // The Perl version does `my $samples = sprintf "%.0f", ($etime - $stime) * $factor;`, + // but this can format in strange ways as shown in these examples: + // `sprintf "%.0f", 1.5` produces "2" + // `sprintf "%.0f", 2.5` produces "2" + // `sprintf "%.0f", 3.5` produces "4" + let samples = ((frame.end_time - frame.start_time) as f64 * opt.factor).round() as usize; // add thousands separators to `samples` let _ = samples_txt_buffer.write_formatted(&samples, &Locale::en); @@ -333,7 +352,7 @@ where let info = if frame.location.function.is_empty() && frame.location.depth == 0 { write!(buffer, "all ({} samples, 100%)", samples_txt) } else { - let pct = (100 * samples) as f64 / timemax as f64; + let pct = (100 * samples) as f64 / (timemax as f64 * opt.factor); let function = deannotate(&frame.location.function); match frame.delta { None => write!( @@ -351,7 +370,7 @@ where if opt.negate_differentials { delta = -delta; } - let delta_pct = (100 * delta) as f64 / timemax as f64; + let delta_pct = (100 * delta) as f64 / (timemax as f64 * opt.factor); write!( buffer, "{} ({} samples, {:.2}%; {:+.2}%)", diff --git a/tests/data/factor/factor_2.5.svg b/tests/data/factor/factor_2.5.svg new file mode 100644 index 00000000..603ecb5b --- /dev/null +++ b/tests/data/factor/factor_2.5.svg @@ -0,0 +1,300 @@ +Flame Graph Reset ZoomSearch read (3 samples, 0.42%)check_events (3 samples, 0.42%)hypercall_page (3 samples, 0.42%)ScavengeRootsTask::do_it (3 samples, 0.42%)ClassLoaderDataGraph::oops_do (3 samples, 0.42%)ClassLoaderData::oops_do (3 samples, 0.42%)PSScavengeKlassClosure::do_klass (3 samples, 0.42%)PSPromotionManager::drain_stacks_depth (3 samples, 0.42%)oopDesc* PSPromotionManager::copy_to_survivor_space<false> (3 samples, 0.42%)InstanceKlass::oop_push_contents (3 samples, 0.42%)ParallelTaskTerminator::offer_termination (13 samples, 1.82%)GCTaskThread::run (35 samples, 4.91%)GCTask..StealTask::do_it (33 samples, 4.63%)Steal..SpinPause (18 samples, 2.53%)Sp..io/netty/buffer/AbstractByteBufAllocator:.directBuffer (3 samples, 0.42%)io/netty/buffer/AbstractReferenceCountedByteBuf:.release (3 samples, 0.42%)io/netty/buffer/PooledByteBuf:.internalNioBuffer (3 samples, 0.42%)sun/nio/ch/NativeThread:.current (3 samples, 0.42%) (8 samples, 1.12%)Java_sun_nio_ch_FileDispatcherImpl_write0 (3 samples, 0.42%)sys_write (3 samples, 0.42%)fget_light (3 samples, 0.42%)__srcu_read_lock (3 samples, 0.42%)__tcp_push_pending_frames (3 samples, 0.42%)ktime_get_real (3 samples, 0.42%)skb_clone (3 samples, 0.42%)tcp_set_skb_tso_segs (3 samples, 0.42%)dev_hard_start_xmit (3 samples, 0.42%)dev_pick_tx (3 samples, 0.42%)dev_queue_xmit_nit (3 samples, 0.42%)xen_restore_fl_direct (3 samples, 0.42%)dev_hard_start_xmit (10 samples, 1.40%)loopback_xmit (8 samples, 1.12%)netif_rx (5 samples, 0.70%)netif_rx.part.82 (5 samples, 0.70%)xen_restore_fl_direct_end (3 samples, 0.42%)dma_issue_pending_all (3 samples, 0.42%)__inet_lookup_established (8 samples, 1.12%)tcp_event_data_recv (3 samples, 0.42%)sock_def_readable (48 samples, 6.74%)sock_def_..__wake_up_sync_key (48 samples, 6.74%)__wake_up..check_events (48 samples, 6.74%)check_eve..hypercall_page (48 samples, 6.74%)hypercall..bictcp_acked (3 samples, 0.42%)ktime_get_real (5 samples, 0.70%)getnstimeofday (5 samples, 0.70%)xen_clocksource_get_cycles (3 samples, 0.42%)xen_clocksource_read (3 samples, 0.42%)tcp_rtt_estimator (3 samples, 0.42%)ip_local_deliver (85 samples, 11.93%)ip_local_deliverip_local_deliver_finish (85 samples, 11.93%)ip_local_deliver_..tcp_v4_rcv (83 samples, 11.65%)tcp_v4_rcvtcp_v4_do_rcv (73 samples, 10.25%)tcp_v4_do_rcvtcp_rcv_established (70 samples, 9.82%)tcp_rcv_establ..tcp_ack (23 samples, 3.23%)tcp..tcp_clean_rtx_queue (15 samples, 2.11%)t..tcp_valid_rtt_meas (3 samples, 0.42%)tcp_rtt_estimator (3 samples, 0.42%)__do_softirq (93 samples, 13.05%)__do_softirqnet_rx_action (93 samples, 13.05%)net_rx_actionprocess_backlog (90 samples, 12.63%)process_backlog__netif_receive_skb (90 samples, 12.63%)__netif_receive_skbip_rcv (88 samples, 12.35%)ip_rcvip_rcv_finish (88 samples, 12.35%)ip_rcv_finiship_local_deliver_finish (3 samples, 0.42%)local_bh_enable (95 samples, 13.33%)local_bh_enabledo_softirq (95 samples, 13.33%)do_softirqcall_softirq (95 samples, 13.33%)call_softirqrcu_bh_qs (3 samples, 0.42%)ip_local_out (113 samples, 15.86%)ip_local_outip_output (113 samples, 15.86%)ip_outputip_finish_output (113 samples, 15.86%)ip_finish_outputdev_queue_xmit (108 samples, 15.16%)dev_queue_xmitnetif_skb_features (3 samples, 0.42%)ip_queue_xmit (118 samples, 16.56%)ip_queue_xmitip_output (5 samples, 0.70%)pvclock_clocksource_read (3 samples, 0.42%)getnstimeofday (5 samples, 0.70%)xen_clocksource_get_cycles (5 samples, 0.70%)xen_clocksource_read (3 samples, 0.42%)pvclock_clocksource_read (3 samples, 0.42%)ktime_get_real (8 samples, 1.12%)xen_clocksource_get_cycles (3 samples, 0.42%)__tcp_push_pending_frames (140 samples, 19.65%)__tcp_push_pending_framestcp_write_xmit (140 samples, 19.65%)tcp_write_xmittcp_transmit_skb (133 samples, 18.67%)tcp_transmit_skbskb_dst_set_noref (3 samples, 0.42%)lock_sock_nested (3 samples, 0.42%)_raw_spin_lock_bh (3 samples, 0.42%)local_bh_disable (3 samples, 0.42%)__kmalloc_node_track_caller (5 samples, 0.70%)arch_local_irq_save (3 samples, 0.42%)__phys_addr (3 samples, 0.42%)get_slab (5 samples, 0.70%)__alloc_skb (18 samples, 2.53%)__..kmem_cache_alloc_node (3 samples, 0.42%)sk_stream_alloc_skb (20 samples, 2.81%)sk..ksize (3 samples, 0.42%)ipv4_mtu (3 samples, 0.42%)tcp_current_mss (5 samples, 0.70%)tcp_established_options (3 samples, 0.42%)tcp_send_mss (8 samples, 1.12%)tcp_xmit_size_goal (3 samples, 0.42%)do_sync_write (180 samples, 25.26%)do_sync_writesock_aio_write (180 samples, 25.26%)sock_aio_writedo_sock_write.isra.10 (180 samples, 25.26%)do_sock_write.isra.10inet_sendmsg (180 samples, 25.26%)inet_sendmsgtcp_sendmsg (178 samples, 24.98%)tcp_sendmsgtcp_xmit_size_goal (3 samples, 0.42%)fsnotify (5 samples, 0.70%)__srcu_read_lock (3 samples, 0.42%)apparmor_file_permission (3 samples, 0.42%)rw_verify_area (8 samples, 1.12%)security_file_permission (3 samples, 0.42%)apparmor_file_permission (3 samples, 0.42%)common_file_perm (3 samples, 0.42%)sun/nio/ch/FileDispatcherImpl:.write0 (218 samples, 30.60%)sun/nio/ch/FileDispatcherImpl:.write0write (205 samples, 28.77%)writesystem_call_fastpath (200 samples, 28.07%)system_call_fastpathsys_write (200 samples, 28.07%)sys_writevfs_write (198 samples, 27.79%)vfs_writesock_aio_write (3 samples, 0.42%)sun/nio/ch/SocketChannelImpl:.write (220 samples, 30.88%)sun/nio/ch/SocketChannelImpl:.writesun/nio/ch/SocketChannelImpl:.writerCleanup (3 samples, 0.42%)io/netty/buffer/PooledUnsafeDirectByteBuf:.readBytes (228 samples, 32.00%)io/netty/buffer/PooledUnsafeDirectByteBuf:.readBytessun/nio/ch/SocketChannelImpl:.writerCleanup (3 samples, 0.42%)io/netty/channel/AbstractChannelHandlerContext:.flush (238 samples, 33.40%)io/netty/channel/AbstractChannelHandlerContext:.flushio/netty/channel/ChannelDuplexHandler:.flush (238 samples, 33.40%)io/netty/channel/ChannelDuplexHandler:.flushio/netty/channel/AbstractChannelHandlerContext:.flush (238 samples, 33.40%)io/netty/channel/AbstractChannelHandlerContext:.flushio/netty/channel/ChannelOutboundHandlerAdapter:.flush (238 samples, 33.40%)io/netty/channel/ChannelOutboundHandlerAdapter:.flushio/netty/channel/AbstractChannelHandlerContext:.flush (238 samples, 33.40%)io/netty/channel/AbstractChannelHandlerContext:.flushio/netty/channel/DefaultChannelPipeline$HeadContext:.flush (238 samples, 33.40%)io/netty/channel/DefaultChannelPipeline$HeadContext:...io/netty/channel/AbstractChannel$AbstractUnsafe:.flush0 (238 samples, 33.40%)io/netty/channel/AbstractChannel$AbstractUnsafe:.flus..io/netty/channel/nio/AbstractNioByteChannel:.doWrite (238 samples, 33.40%)io/netty/channel/nio/AbstractNioByteChannel:.doWriteio/netty/util/Recycler:.recycle (3 samples, 0.42%)io/netty/channel/AbstractChannelHandlerContext:.fireChannelReadComplete (243 samples, 34.11%)io/netty/channel/AbstractChannelHandlerContext:.fireCh..org/vertx/java/core/net/impl/VertxHandler:.channelReadComplete (243 samples, 34.11%)org/vertx/java/core/net/impl/VertxHandler:.channelRead..io/netty/channel/ChannelDuplexHandler:.flush (5 samples, 0.70%)io/netty/channel/AbstractChannelHandlerContext:.fireChannelReadComplete (245 samples, 34.39%)io/netty/channel/AbstractChannelHandlerContext:.fireCha..io/netty/handler/codec/ByteToMessageDecoder:.channelReadComplete (245 samples, 34.39%)io/netty/handler/codec/ByteToMessageDecoder:.channelRea..org/vertx/java/core/net/impl/VertxHandler:.channelReadComplete (3 samples, 0.42%)io/netty/buffer/AbstractReferenceCountedByteBuf:.release (3 samples, 0.42%)java/util/concurrent/ConcurrentHashMap:.get (3 samples, 0.42%)org/mozilla/javascript/Context:.getWrapFactory (5 samples, 0.70%)org/mozilla/javascript/ScriptableObject:.getParentScope (3 samples, 0.42%)org/mozilla/javascript/WrapFactory:.wrapAsJavaObject (5 samples, 0.70%)java/util/HashMap:.get (3 samples, 0.42%)org/mozilla/javascript/WrapFactory:.wrap (3 samples, 0.42%)java/util/HashMap:.get (3 samples, 0.42%)org/mozilla/javascript/ScriptableObject$RelinkedSlot:.getValue (3 samples, 0.42%)org/mozilla/javascript/ScriptRuntime:.getObjectProp (10 samples, 1.40%)vtable chunks (3 samples, 0.42%)org/mozilla/javascript/ScriptRuntime:.nameOrFunction (3 samples, 0.42%)org/mozilla/javascript/ScriptableObject$Slot:.getValue (3 samples, 0.42%)org/mozilla/javascript/ScriptRuntime:.name (3 samples, 0.42%)org/mozilla/javascript/IdScriptableObject:.get (3 samples, 0.42%)org/mozilla/javascript/ScriptRuntime:.getPropFunctionAndThis (3 samples, 0.42%)org/mozilla/javascript/IdScriptableObject:.findInstanceIdInfo (3 samples, 0.42%)org/mozilla/javascript/IdScriptableObject:.has (8 samples, 1.12%)org/mozilla/javascript/ScriptableObject:.getSlot (5 samples, 0.70%)org/mozilla/javascript/IdScriptableObject:.put (3 samples, 0.42%)org/mozilla/javascript/ScriptableObject:.getSlot (3 samples, 0.42%)org/mozilla/javascript/IdScriptableObject:.setAttributes (3 samples, 0.42%)org/mozilla/javascript/MemberBox:.invoke (3 samples, 0.42%)org/mozilla/javascript/NativeJavaMethod:.call (5 samples, 0.70%)org/mozilla/javascript/WrapFactory:.wrap (3 samples, 0.42%)org/mozilla/javascript/NativeJavaMethod:.findFunction (3 samples, 0.42%)org/mozilla/javascript/NativeJavaObject:.get (5 samples, 0.70%)org/mozilla/javascript/IdScriptableObject:.get (3 samples, 0.42%)org/mozilla/javascript/ScriptableObject:.getSlot (3 samples, 0.42%)org/mozilla/javascript/IdScriptableObject:.put (5 samples, 0.70%)org/mozilla/javascript/ScriptableObject:.getSlot (5 samples, 0.70%)org/mozilla/javascript/ScriptableObject:.createSlot (5 samples, 0.70%)org/mozilla/javascript/IdScriptableObject:.setAttributes (3 samples, 0.42%)org/mozilla/javascript/ScriptRuntime:.createFunctionActivation (13 samples, 1.82%)org/mozilla/javascript/gen/file__home_bgregg_testtest_vert_x_2_1_4_sys_mods_io_vertx_lang_js_1_1_0 (3 samples, 0.42%)org/mozilla/javascript/ScriptRuntime:.getObjectProp (3 samples, 0.42%)org/mozilla/javascript/ScriptableObject$Slot:.getValue (3 samples, 0.42%)org/mozilla/javascript/ScriptRuntime:.getPropFunctionAndThis (3 samples, 0.42%)org/mozilla/javascript/NativeJavaObject:.get (3 samples, 0.42%)java/util/HashMap:.get (3 samples, 0.42%)jint_disjoint_arraycopy (3 samples, 0.42%)org/mozilla/javascript/IdScriptableObject:.get (5 samples, 0.70%)org/mozilla/javascript/IdScriptableObject:.has (3 samples, 0.42%)org/mozilla/javascript/IdScriptableObject:.put (5 samples, 0.70%)org/mozilla/javascript/ScriptableObject:.getSlot (5 samples, 0.70%)org/mozilla/javascript/ScriptableObject:.createSlot (5 samples, 0.70%)org/mozilla/javascript/ScriptRuntime:.createFunctionActivation (13 samples, 1.82%)org/mozilla/javascript/IdScriptableObject:.setAttributes (5 samples, 0.70%)org/mozilla/javascript/ScriptRuntime:.getObjectProp (3 samples, 0.42%)org/mozilla/javascript/IdScriptableObject:.get (3 samples, 0.42%)org/mozilla/javascript/ScriptableObject:.getSlot (3 samples, 0.42%)org/mozilla/javascript/ScriptRuntime:.nameOrFunction (3 samples, 0.42%)org/mozilla/javascript/IdScriptableObject:.get (3 samples, 0.42%)org/mozilla/javascript/ScriptableObject$RelinkedSlot:.getValue (3 samples, 0.42%)org/mozilla/javascript/IdScriptableObject:.findInstanceIdInfo (3 samples, 0.42%)org/mozilla/javascript/IdScriptableObject:.put (10 samples, 1.40%)org/mozilla/javascript/ScriptableObject:.getSlot (8 samples, 1.12%)org/mozilla/javascript/ScriptableObject:.createSlot (8 samples, 1.12%)org/mozilla/javascript/ScriptableObject:.getSlot (3 samples, 0.42%)org/mozilla/javascript/ScriptRuntime:.setObjectProp (18 samples, 2.53%)or..vtable chunks (3 samples, 0.42%)org/mozilla/javascript/NativeFunction:.initScriptFunction (3 samples, 0.42%)org/mozilla/javascript/ScriptRuntime:.createFunctionActivation (3 samples, 0.42%)org/mozilla/javascript/ScriptRuntime:.createFunctionActivation (5 samples, 0.70%)org/mozilla/javascript/IdScriptableObject:.get (3 samples, 0.42%)org/mozilla/javascript/IdScriptableObject:.has (3 samples, 0.42%)org/mozilla/javascript/ScriptRuntime:.setObjectProp (20 samples, 2.81%)or..org/mozilla/javascript/IdScriptableObject:.put (18 samples, 2.53%)or..org/mozilla/javascript/ScriptableObject:.getSlot (15 samples, 2.11%)o..org/mozilla/javascript/ScriptableObject:.createSlot (15 samples, 2.11%)o..org/mozilla/javascript/ScriptRuntime:.newObject (80 samples, 11.23%)org/mozilla/java..org/mozilla/javascript/gen/file__home_bgregg_testtest_vert_x_2_1_4_sys_mods_io_vertx_lang_js_1_1_0 (80 samples, 11.23%)org/mozilla/java..org/mozilla/javascript/gen/file__home_bgregg_testtest_vert_x_2_1_4_sys_mods_io_vertx_lang_js_1_1_0 (80 samples, 11.23%)org/mozilla/java..org/mozilla/javascript/optimizer/OptRuntime:.call2 (33 samples, 4.63%)org/m..org/mozilla/javascript/gen/file__home_bgregg_testtest_vert_x_2_1_4_sys_mods_io_vertx_lang_js_1_1_0 (28 samples, 3.93%)org/..org/mozilla/javascript/ScriptableObject:.getParentScope (3 samples, 0.42%)org/mozilla/javascript/IdScriptableObject:.has (23 samples, 3.23%)org..org/mozilla/javascript/ScriptableObject:.getSlot (13 samples, 1.82%)org/mozilla/javascript/ScriptRuntime:.setObjectProp (43 samples, 6.04%)org/mozi..org/mozilla/javascript/IdScriptableObject:.put (20 samples, 2.81%)or..org/mozilla/javascript/ScriptableObject:.getSlot (18 samples, 2.53%)or..org/mozilla/javascript/ScriptableObject:.createSlot (15 samples, 2.11%)o..org/mozilla/javascript/ScriptableObject:.getPrototype (3 samples, 0.42%)org/mozilla/javascript/ScriptableObject:.getParentScope (3 samples, 0.42%)org/mozilla/javascript/gen/file__home_bgregg_testtest_vert_x_2_1_4_sys_mods_io_vertx_lang_js_1_1_0 (8 samples, 1.12%)org/mozilla/javascript/gen/file__home_bgregg_testtest_vert_x_2_1_4_sys_mods_io_vertx_lang_js_1_1_0 (8 samples, 1.12%)org/mozilla/javascript/TopLevel:.getBuiltinPrototype (5 samples, 0.70%)org/mozilla/javascript/ScriptRuntime:.name (3 samples, 0.42%)org/mozilla/javascript/ScriptRuntime:.nameOrFunction (3 samples, 0.42%)vtable chunks (3 samples, 0.42%)org/mozilla/javascript/ScriptRuntime:.setObjectProp (3 samples, 0.42%)org/mozilla/javascript/IdScriptableObject:.has (3 samples, 0.42%)org/mozilla/javascript/ScriptableObject:.getSlot (3 samples, 0.42%)org/mozilla/javascript/IdScriptableObject:.setAttributes (5 samples, 0.70%)org/mozilla/javascript/ScriptRuntime:.createFunctionActivation (13 samples, 1.82%)org/mozilla/javascript/TopLevel:.getBuiltinPrototype (5 samples, 0.70%)org/mozilla/javascript/gen/file__home_bgregg_testtest_vert_x_2_1_4_sys_mods_io_vertx_lang_js_1_1_0 (205 samples, 28.77%)org/mozilla/javascript/gen/file__home_bgregg_..org/mozilla/javascript/gen/file__home_bgregg_testtest_vert_x_2_1_4_sys_mods_io_vertx_lang_js_1_1_0 (203 samples, 28.49%)org/mozilla/javascript/gen/file__home_bgregg_..org/mozilla/javascript/optimizer/OptRuntime:.call2 (23 samples, 3.23%)org..org/mozilla/javascript/gen/file__home_bgregg_testtest_vert_x_2_1_4_sys_mods_io_vertx_lang_js_1_1_0 (15 samples, 2.11%)o..org/mozilla/javascript/ScriptRuntime:.setObjectProp (3 samples, 0.42%)org/mozilla/javascript/IdScriptableObject:.put (3 samples, 0.42%)org/mozilla/javascript/ScriptableObject:.getSlot (3 samples, 0.42%)org/mozilla/javascript/ScriptableObject:.createSlot (3 samples, 0.42%)org/mozilla/javascript/ScriptRuntime:.indexFromString (3 samples, 0.42%)org/mozilla/javascript/ScriptRuntime:.setObjectElem (5 samples, 0.70%)org/mozilla/javascript/ScriptRuntime:.indexFromString (5 samples, 0.70%)io/netty/handler/codec/http/DefaultHttpHeaders:.set (3 samples, 0.42%)io/netty/buffer/AbstractByteBuf:.writeBytes (3 samples, 0.42%)io/netty/buffer/AbstractByteBuf:.writeBytes (3 samples, 0.42%)io/netty/buffer/AbstractByteBufAllocator:.directBuffer (8 samples, 1.12%)io/netty/util/concurrent/FastThreadLocal:.get (3 samples, 0.42%)io/netty/handler/codec/http/HttpObjectEncoder:.encode (13 samples, 1.82%)java/util/ArrayList:.add (3 samples, 0.42%)io/netty/util/internal/RecyclableArrayList:.newInstance (3 samples, 0.42%)io/netty/util/concurrent/FastThreadLocal:.get (3 samples, 0.42%)java/util/ArrayList:.ensureExplicitCapacity (3 samples, 0.42%)io/netty/channel/AbstractChannelHandlerContext:.write (30 samples, 4.21%)io/n..org/vertx/java/core/http/impl/VertxHttpHandler:.write (28 samples, 3.93%)org/..io/netty/channel/AbstractChannelHandlerContext:.write (25 samples, 3.51%)io/..io/netty/handler/codec/MessageToMessageEncoder:.write (25 samples, 3.51%)io/..vtable chunks (3 samples, 0.42%)io/netty/channel/AbstractChannelHandlerContext:.write (33 samples, 4.63%)io/ne..org/vertx/java/core/http/impl/VertxHttpHandler:.write (3 samples, 0.42%)io/netty/handler/codec/http/DefaultHttpHeaders:.add0 (3 samples, 0.42%)io/netty/handler/codec/http/DefaultHttpHeaders:.set (3 samples, 0.42%)org/mozilla/javascript/NativeJavaMethod:.call (53 samples, 7.44%)org/mozill..org/mozilla/javascript/MemberBox:.invoke (53 samples, 7.44%)org/mozill..sun/reflect/DelegatingMethodAccessorImpl:.invoke (48 samples, 6.74%)sun/refle..sun/nio/cs/UTF_8$Encoder:.<init> (3 samples, 0.42%)jbyte_disjoint_arraycopy (3 samples, 0.42%)io/netty/channel/AbstractChannelHandlerContext:.fireChannelRead (320 samples, 44.91%)io/netty/channel/AbstractChannelHandlerContext:.fireChannelReadorg/vertx/java/core/net/impl/VertxHandler:.channelRead (320 samples, 44.91%)org/vertx/java/core/net/impl/VertxHandler:.channelReadorg/vertx/java/core/http/impl/DefaultHttpServer$ServerHandler:.doMessageReceived (310 samples, 43.51%)org/vertx/java/core/http/impl/DefaultHttpServer$ServerHandler:.doMessa..org/mozilla/javascript/gen/file__home_bgregg_testtest_vert_x_2_1_4_sys_mods_io_vertx_lang_js_1_1_0 (293 samples, 41.12%)org/mozilla/javascript/gen/file__home_bgregg_testtest_vert_x_2_1_4..org/mozilla/javascript/gen/file__home_bgregg_testtest_vert_x_2_1_4_sys_mods_io_vertx_lang_js_1_1_0 (290 samples, 40.70%)org/mozilla/javascript/gen/file__home_bgregg_testtest_vert_x_2_1_..org/mozilla/javascript/gen/file__home_bgregg_testtest_vhello_js_1:.call (68 samples, 9.54%)org/mozilla/j..org/mozilla/javascript/gen/file__home_bgregg_testtest_vert_x_2_1_4_sys_mods_io_vertx_lang_js_1_1_0 (60 samples, 8.42%)org/mozilla..org/mozilla/javascript/ScriptRuntime:.name (3 samples, 0.42%)org/mozilla/javascript/ScriptRuntime:.nameOrFunction (3 samples, 0.42%)org/mozilla/javascript/IdScriptableObject:.get (3 samples, 0.42%)org/mozilla/javascript/ScriptableObject$RelinkedSlot:.getValue (3 samples, 0.42%)io/netty/buffer/AbstractByteBuf:.forEachByteAsc0 (3 samples, 0.42%)io/netty/util/internal/AppendableCharSequence:.append (3 samples, 0.42%)io/netty/handler/codec/http/HttpHeaders:.isTransferEncodingChunked (3 samples, 0.42%)io/netty/handler/codec/http/HttpObjectDecoder:.findWhitespace (3 samples, 0.42%)io/netty/buffer/AbstractByteBuf:.forEachByteAsc0 (5 samples, 0.70%)io/netty/handler/codec/http/HttpHeaders:.hash (3 samples, 0.42%)io/netty/handler/codec/http/HttpObjectDecoder:.splitHeader (13 samples, 1.82%)io/netty/channel/AbstractChannelHandlerContext:.fireChannelRead (358 samples, 50.25%)io/netty/channel/AbstractChannelHandlerContext:.fireChannelReadio/netty/handler/codec/ByteToMessageDecoder:.channelRead (358 samples, 50.25%)io/netty/handler/codec/ByteToMessageDecoder:.channelReadio/netty/handler/codec/http/HttpObjectDecoder:.decode (33 samples, 4.63%)io/ne..io/netty/handler/codec/http/HttpObjectDecoder:.readHeaders (25 samples, 3.51%)io/..java/util/Arrays:.fill (3 samples, 0.42%)java/nio/channels/spi/AbstractInterruptibleChannel:.end (3 samples, 0.42%)sys_read (3 samples, 0.42%)do_sync_read (3 samples, 0.42%)__kfree_skb (3 samples, 0.42%)tcp_rcv_space_adjust (3 samples, 0.42%)skb_release_data (3 samples, 0.42%)__kfree_skb (5 samples, 0.70%)skb_release_head_state (3 samples, 0.42%)dst_release (3 samples, 0.42%)_raw_spin_lock_bh (3 samples, 0.42%)skb_copy_datagram_iovec (5 samples, 0.70%)copy_user_enhanced_fast_string (3 samples, 0.42%)do_sync_read (23 samples, 3.23%)do_..sock_aio_read (23 samples, 3.23%)soc..sock_aio_read.part.13 (23 samples, 3.23%)soc..do_sock_read.isra.12 (23 samples, 3.23%)do_..inet_recvmsg (23 samples, 3.23%)ine..tcp_recvmsg (18 samples, 2.53%)tc..tcp_cleanup_rbuf (5 samples, 0.70%)__tcp_select_window (3 samples, 0.42%)io/netty/channel/socket/nio/NioSocketChannel:.doReadBytes (40 samples, 5.61%)io/nett..sun/nio/ch/SocketChannelImpl:.read (40 samples, 5.61%)sun/nio..sun/nio/ch/FileDispatcherImpl:.read0 (38 samples, 5.33%)sun/ni..read (38 samples, 5.33%)readsystem_call_fastpath (30 samples, 4.21%)syst..sys_read (30 samples, 4.21%)sys_..vfs_read (25 samples, 3.51%)vfs..rw_verify_area (3 samples, 0.42%)io/netty/channel/nio/AbstractNioByteChannel$NioByteUnsafe:.read (650 samples, 91.23%)io/netty/channel/nio/AbstractNioByteChannel$NioByteUnsafe:.readio/netty/handler/codec/ByteToMessageDecoder:.channelReadComplete (3 samples, 0.42%)JavaThread::run (658 samples, 92.35%)JavaThread::runJavaThread::thread_main_inner (658 samples, 92.35%)JavaThread::thread_main_innerthread_entry (658 samples, 92.35%)thread_entryJavaCalls::call_virtual (658 samples, 92.35%)JavaCalls::call_virtualJavaCalls::call_virtual (658 samples, 92.35%)JavaCalls::call_virtualJavaCalls::call_helper (658 samples, 92.35%)JavaCalls::call_helpercall_stub (658 samples, 92.35%)call_stubInterpreter (658 samples, 92.35%)InterpreterInterpreter (658 samples, 92.35%)Interpreterio/netty/channel/nio/NioEventLoop:.run (658 samples, 92.35%)io/netty/channel/nio/NioEventLoop:.runio/netty/channel/nio/NioEventLoop:.processSelectedKeys (655 samples, 91.93%)io/netty/channel/nio/NioEventLoop:.processSelectedKeysio/netty/channel/nio/NioEventLoop:.processSelectedKeysOptimized (655 samples, 91.93%)io/netty/channel/nio/NioEventLoop:.processSelectedKeysOptimizedio/netty/channel/nio/NioEventLoop:.processSelectedKey (653 samples, 91.65%)io/netty/channel/nio/NioEventLoop:.processSelectedKeyio/netty/channel/socket/nio/NioSocketChannel:.doReadBytes (3 samples, 0.42%)PSIsAliveClosure::do_object_b (3 samples, 0.42%)StringTable::unlink_or_oops_do (5 samples, 0.70%)start_thread (703 samples, 98.67%)start_threadjava_start (703 samples, 98.67%)java_startVMThread::run (10 samples, 1.40%)VMThread::loop (10 samples, 1.40%)VMThread::evaluate_operation (10 samples, 1.40%)VM_Operation::evaluate (10 samples, 1.40%)VM_ParallelGCFailedAllocation::doit (10 samples, 1.40%)ParallelScavengeHeap::failed_mem_allocate (10 samples, 1.40%)PSScavenge::invoke (10 samples, 1.40%)PSScavenge::invoke_no_policy (10 samples, 1.40%)pthread_cond_signal@@GLIBC_2.3.2 (3 samples, 0.42%)system_call_fastpath (3 samples, 0.42%)sys_futex (3 samples, 0.42%)do_futex (3 samples, 0.42%)futex_wake_op (3 samples, 0.42%)all (713 samples, 100%)java (713 samples, 100.07%)javawrite (8 samples, 1.12%)check_events (8 samples, 1.12%)hypercall_page (8 samples, 1.12%) \ No newline at end of file diff --git a/tests/flamegraph-differential.rs b/tests/flamegraph-differential.rs deleted file mode 100644 index 1284e4b9..00000000 --- a/tests/flamegraph-differential.rs +++ /dev/null @@ -1,64 +0,0 @@ -#[macro_use] -extern crate pretty_assertions; - -extern crate inferno; - -use inferno::flamegraph::{self, Options}; -use std::fs::{self, File}; -use std::io::{BufRead, BufReader, Cursor}; - -fn test_differential(input_file: &str, expected_result_file: &str, options: Options) { - let r = File::open(input_file).unwrap(); - let expected_len = fs::metadata(expected_result_file).unwrap().len() as usize; - let mut result = Cursor::new(Vec::with_capacity(expected_len)); - flamegraph::from_reader(options, r, &mut result).unwrap(); - let mut expected = BufReader::new(File::open(expected_result_file).unwrap()); - - result.set_position(0); - - let mut buf = String::new(); - let mut line_num = 1; - for line in result.lines() { - if expected.read_line(&mut buf).unwrap() == 0 { - panic!( - "\noutput has more lines than expected result file: {}", - expected_result_file - ); - } - assert_eq!( - line.unwrap(), - buf.trim_end(), - "\n{}:{}", - expected_result_file, - line_num - ); - buf.clear(); - line_num += 1; - } - - if expected.read_line(&mut buf).unwrap() > 0 { - panic!( - "\n{} has more lines than output, beginning at line: {}", - expected_result_file, line_num - ) - } -} - -#[test] -fn flamegraph_differential() { - let input_file = "./tests/data/differential/perf-cycles-instructions-01-collapsed-all-diff.txt"; - let expected_result_file = "./tests/data/differential/diff.svg"; - let options = Default::default(); - test_differential(input_file, expected_result_file, options); -} - -#[test] -fn flamegraph_differential_negated() { - let input_file = "./tests/data/differential/perf-cycles-instructions-01-collapsed-all-diff.txt"; - let expected_result_file = "./tests/data/differential/diff_negated.svg"; - let options = Options { - negate_differentials: true, - ..Default::default() - }; - test_differential(input_file, expected_result_file, options); -} diff --git a/tests/flamegraph-nameattr.rs b/tests/flamegraph-nameattr.rs deleted file mode 100644 index 455ed90a..00000000 --- a/tests/flamegraph-nameattr.rs +++ /dev/null @@ -1,59 +0,0 @@ -#[macro_use] -extern crate pretty_assertions; - -extern crate inferno; - -use inferno::flamegraph; -use std::fs::{self, File}; -use std::io::{BufRead, BufReader, Cursor}; -use std::path::PathBuf; - -#[test] -fn flamegraph_nameattr() { - let input_file = "./flamegraph/test/results/perf-cycles-instructions-01-collapsed-all.txt"; - let expected_result_file = "./tests/data/nameattr/nameattr.svg"; - let nameattr_file = "./tests/data/nameattr/nameattr.txt"; - - let options = flamegraph::Options { - hash: true, - func_frameattrs: flamegraph::FuncFrameAttrsMap::from_file(&PathBuf::from(nameattr_file)) - .unwrap(), - title: "Flame Graph".to_string(), - ..Default::default() - }; - - let r = File::open(input_file).unwrap(); - let expected_len = fs::metadata(expected_result_file).unwrap().len() as usize; - let mut result = Cursor::new(Vec::with_capacity(expected_len)); - flamegraph::from_reader(options, r, &mut result).unwrap(); - let mut expected = BufReader::new(File::open(expected_result_file).unwrap()); - - result.set_position(0); - - let mut buf = String::new(); - let mut line_num = 1; - for line in result.lines() { - if expected.read_line(&mut buf).unwrap() == 0 { - panic!( - "\noutput has more lines than expected result file: {}", - expected_result_file - ); - } - assert_eq!( - line.unwrap(), - buf.trim_end(), - "\n{}:{}", - expected_result_file, - line_num - ); - buf.clear(); - line_num += 1; - } - - if expected.read_line(&mut buf).unwrap() > 0 { - panic!( - "\n{} has more lines than output, beginning at line: {}", - expected_result_file, line_num - ) - } -} diff --git a/tests/flamegraph-colors.rs b/tests/flamegraph.rs similarity index 51% rename from tests/flamegraph-colors.rs rename to tests/flamegraph.rs index 7804e3d7..51fca472 100644 --- a/tests/flamegraph-colors.rs +++ b/tests/flamegraph.rs @@ -1,16 +1,12 @@ -#[macro_use] -extern crate pretty_assertions; - extern crate inferno; -use inferno::flamegraph; -use inferno::flamegraph::BackgroundColor; -use inferno::flamegraph::Palette; +use inferno::flamegraph::{self, BackgroundColor, Options, Palette}; use std::fs::{self, File}; use std::io::{BufRead, BufReader, Cursor}; +use std::path::PathBuf; use std::str::FromStr; -fn do_test(input_file: &str, expected_result_file: &str, options: flamegraph::Options) { +fn test_flamegraph(input_file: &str, expected_result_file: &str, options: Options) { let r = File::open(input_file).unwrap(); let expected_len = fs::metadata(expected_result_file).unwrap().len() as usize; let mut result = Cursor::new(Vec::with_capacity(expected_len)); @@ -21,7 +17,6 @@ fn do_test(input_file: &str, expected_result_file: &str, options: flamegraph::Op let mut buf = String::new(); let mut line_num = 1; - for line in result.lines() { if expected.read_line(&mut buf).unwrap() == 0 { panic!( @@ -61,7 +56,7 @@ fn flamegraph_colors_java() { ..Default::default() }; - do_test(input_file, expected_result_file, options) + test_flamegraph(input_file, expected_result_file, options) } #[test] @@ -77,5 +72,53 @@ fn flamegraph_colors_js() { ..Default::default() }; - do_test(input_file, expected_result_file, options) + test_flamegraph(input_file, expected_result_file, options) +} + +#[test] +fn flamegraph_differential() { + let input_file = "./tests/data/differential/perf-cycles-instructions-01-collapsed-all-diff.txt"; + let expected_result_file = "./tests/data/differential/diff.svg"; + let options = Default::default(); + test_flamegraph(input_file, expected_result_file, options); +} + +#[test] +fn flamegraph_differential_negated() { + let input_file = "./tests/data/differential/perf-cycles-instructions-01-collapsed-all-diff.txt"; + let expected_result_file = "./tests/data/differential/diff_negated.svg"; + let options = Options { + negate_differentials: true, + ..Default::default() + }; + test_flamegraph(input_file, expected_result_file, options); +} + +#[test] +fn flamegraph_factor() { + let input_file = "./flamegraph/test/results/perf-vertx-stacks-01-collapsed-all.txt"; + let expected_result_file = "./tests/data/factor/factor_2.5.svg"; + let options = Options { + factor: 2.5, + hash: true, + ..Default::default() + }; + test_flamegraph(input_file, expected_result_file, options); +} + +#[test] +fn flamegraph_nameattr() { + let input_file = "./flamegraph/test/results/perf-cycles-instructions-01-collapsed-all.txt"; + let expected_result_file = "./tests/data/nameattr/nameattr.svg"; + let nameattr_file = "./tests/data/nameattr/nameattr.txt"; + + let options = flamegraph::Options { + hash: true, + func_frameattrs: flamegraph::FuncFrameAttrsMap::from_file(&PathBuf::from(nameattr_file)) + .unwrap(), + title: "Flame Graph".to_string(), + ..Default::default() + }; + + test_flamegraph(input_file, expected_result_file, options); }