pstats: Allow scrolling through frames in flame charts

Closes #1658
This commit is contained in:
rdb 2024-10-28 11:46:58 +01:00
parent 71e2b7fff6
commit c48721493a
25 changed files with 419 additions and 75 deletions

View File

@ -226,7 +226,7 @@ add_view(GtkWidget *parent_menu, const PStatViewLevel *view_level,
}
else if (menu_item != nullptr && menu == nullptr) {
// Unhook the signal handler, we are creating a submenu.
GtkStatsMonitor::MenuDef smd(_thread_index, collector, GtkStatsMonitor::CT_strip_chart, show_level);
GtkStatsMonitor::MenuDef smd(GtkStatsMonitor::CT_strip_chart, _thread_index, collector, -1, show_level);
const GtkStatsMonitor::MenuDef *menu_def = _monitor->add_menu(smd);
g_signal_handlers_disconnect_by_data(G_OBJECT(menu_item), (void *)menu_def);
@ -277,7 +277,7 @@ add_view(GtkWidget *parent_menu, const PStatViewLevel *view_level,
GtkWidget *GtkStatsChartMenu::
make_menu_item(const char *label, int collector_index, ChartType chart_type,
bool show_level) {
GtkStatsMonitor::MenuDef smd(_thread_index, collector_index, chart_type, show_level);
GtkStatsMonitor::MenuDef smd(chart_type, _thread_index, collector_index, -1, show_level);
const GtkStatsMonitor::MenuDef *menu_def = _monitor->add_menu(smd);
GtkWidget *menu_item = gtk_menu_item_new_with_label(label);

View File

@ -24,13 +24,16 @@ static const int default_flame_graph_height = 150;
*/
GtkStatsFlameGraph::
GtkStatsFlameGraph(GtkStatsMonitor *monitor, int thread_index,
int collector_index) :
PStatFlameGraph(monitor, thread_index, collector_index, 0, 0),
int collector_index, int frame_number) :
PStatFlameGraph(monitor, thread_index, collector_index, frame_number, 0, 0),
GtkStatsGraph(monitor, false)
{
// Let's show the units on the guide bar labels. There's room.
set_guide_bar_units(get_guide_bar_units() | GBU_show_units);
std::string window_title = get_title_text();
gtk_window_set_title(GTK_WINDOW(_window), window_title.c_str());
// Put some stuff on top of the graph.
_top_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_box_pack_start(GTK_BOX(_graph_vbox), _top_hbox,
@ -51,6 +54,13 @@ GtkStatsFlameGraph(GtkStatsMonitor *monitor, int thread_index,
gtk_box_pack_start(GTK_BOX(_top_hbox), _scale_area, TRUE, TRUE, 0);
gtk_box_pack_end(GTK_BOX(_top_hbox), _total_label, FALSE, FALSE, 0);
// Listen for mouse scroll and keyboard events.
gtk_widget_add_events(_window, GDK_SCROLL_MASK | GDK_KEY_PRESS_MASK);
g_signal_connect(G_OBJECT(_window), "scroll_event",
G_CALLBACK(scroll_callback), this);
g_signal_connect(G_OBJECT(_window), "key_press_event",
G_CALLBACK(key_press_callback), this);
gtk_widget_set_size_request(_graph_window,
default_flame_graph_width * monitor->get_resolution() / 96,
default_flame_graph_height * monitor->get_resolution() / 96);
@ -284,6 +294,15 @@ draw_bar(int depth, int from_x, int to_x, int collector_index, int parent_index)
pango_layout_set_text(layout, def._name.c_str(), def._name.size());
}
}
else if (collector_index == 0 && get_frame_number() >= 0) {
char text[32];
sprintf(text, "Frame %d", get_frame_number());
pango_layout_set_text(layout, text, -1);
if (pango_layout_is_ellipsized(layout)) {
// Nope, it's too long, go back.
pango_layout_set_text(layout, def._name.c_str(), def._name.size());
}
}
}
int width, height;
@ -429,8 +448,7 @@ handle_button_press(int graph_x, int graph_y, bool double_click, int button) {
{
const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
get_thread_index(), collector_index,
GtkStatsMonitor::CT_strip_chart, false,
GtkStatsMonitor::CT_strip_chart, get_thread_index(), collector_index,
});
GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Strip Chart");
@ -442,8 +460,7 @@ handle_button_press(int graph_x, int graph_y, bool double_click, int button) {
{
const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
get_thread_index(), collector_index,
GtkStatsMonitor::CT_flame_graph,
GtkStatsMonitor::CT_flame_graph, get_thread_index(), collector_index,
});
GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Flame Graph");
@ -460,8 +477,7 @@ handle_button_press(int graph_x, int graph_y, bool double_click, int button) {
{
const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
-1, collector_index,
GtkStatsMonitor::CT_choose_color,
GtkStatsMonitor::CT_choose_color, -1, collector_index,
});
GtkWidget *menu_item = gtk_menu_item_new_with_label("Change Color...");
@ -473,8 +489,7 @@ handle_button_press(int graph_x, int graph_y, bool double_click, int button) {
{
const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
-1, collector_index,
GtkStatsMonitor::CT_reset_color,
GtkStatsMonitor::CT_reset_color, -1, collector_index,
});
GtkWidget *menu_item = gtk_menu_item_new_with_label("Reset Color");
@ -721,3 +736,63 @@ draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data) {
return TRUE;
}
/**
*
*/
gboolean GtkStatsFlameGraph::
scroll_callback(GtkWidget *widget, GdkEventScroll *event, gpointer data) {
GtkStatsFlameGraph *self = (GtkStatsFlameGraph *)data;
bool changed = false;
switch (event->direction) {
case GDK_SCROLL_LEFT:
changed = self->prev_frame();
break;
case GDK_SCROLL_RIGHT:
changed = self->next_frame();
break;
}
if (changed) {
std::string window_title = self->get_title_text();
gtk_window_set_title(GTK_WINDOW(self->_window), window_title.c_str());
return TRUE;
} else {
return FALSE;
}
}
/**
*
*/
gboolean GtkStatsFlameGraph::
key_press_callback(GtkWidget *widget, GdkEventKey *event, gpointer data) {
GtkStatsFlameGraph *self = (GtkStatsFlameGraph *)data;
bool changed = false;
switch (event->keyval) {
case GDK_KEY_Left:
changed = self->prev_frame();
break;
case GDK_KEY_Right:
changed = self->next_frame();
break;
case GDK_KEY_Home:
changed = self->first_frame();
break;
case GDK_KEY_End:
changed = self->last_frame();
break;
}
if (changed) {
std::string window_title = self->get_title_text();
gtk_window_set_title(GTK_WINDOW(self->_window), window_title.c_str());
return TRUE;
} else {
return FALSE;
}
}

View File

@ -28,7 +28,7 @@ class GtkStatsLabel;
class GtkStatsFlameGraph final : public PStatFlameGraph, public GtkStatsGraph {
public:
GtkStatsFlameGraph(GtkStatsMonitor *monitor, int thread_index,
int collector_index=-1);
int collector_index=-1, int frame_number=-1);
virtual ~GtkStatsFlameGraph();
virtual void new_collector(int collector_index);
@ -76,6 +76,8 @@ private:
static void toggled_callback(GtkToggleButton *button, gpointer data);
static gboolean draw_callback(GtkWidget *widget, cairo_t *cr, gpointer data);
static gboolean scroll_callback(GtkWidget *widget, GdkEventScroll *event, gpointer data);
static gboolean key_press_callback(GtkWidget *widget, GdkEventKey *event, gpointer data);
private:
std::string _net_value_text;

View File

@ -15,10 +15,12 @@
*
*/
INLINE GtkStatsMonitor::MenuDef::
MenuDef(int thread_index, int collector_index, ChartType chart_type, bool show_level) :
MenuDef(ChartType chart_type, int thread_index, int collector_index,
int frame_number, bool show_level) :
_chart_type(chart_type),
_thread_index(thread_index),
_collector_index(collector_index),
_chart_type(chart_type),
_frame_number(frame_number),
_show_level(show_level),
_monitor(nullptr)
{
@ -29,14 +31,17 @@ MenuDef(int thread_index, int collector_index, ChartType chart_type, bool show_l
*/
INLINE bool GtkStatsMonitor::MenuDef::
operator < (const MenuDef &other) const {
if (_chart_type != other._chart_type) {
return _chart_type < other._chart_type;
}
if (_thread_index != other._thread_index) {
return _thread_index < other._thread_index;
}
if (_collector_index != other._collector_index) {
return _collector_index < other._collector_index;
}
if (_chart_type != other._chart_type) {
return _chart_type < other._chart_type;
if (_frame_number != other._frame_number) {
return _frame_number < other._frame_number;
}
return (int)_show_level < (int)other._show_level;
}

View File

@ -316,8 +316,8 @@ open_timeline() {
* Opens a new flame graph showing the indicated data.
*/
PStatGraph *GtkStatsMonitor::
open_flame_graph(int thread_index, int collector_index) {
GtkStatsFlameGraph *graph = new GtkStatsFlameGraph(this, thread_index, collector_index);
open_flame_graph(int thread_index, int collector_index, int frame_number) {
GtkStatsFlameGraph *graph = new GtkStatsFlameGraph(this, thread_index, collector_index, frame_number);
add_graph(graph);
return graph;
}
@ -734,7 +734,7 @@ status_bar_button_event(GtkWidget *widget, GdkEventButton *event, gpointer data)
const PStatViewLevel *child_level = view_level->get_child(c);
int child_collector = child_level->get_collector();
const MenuDef *menu_def = monitor->add_menu({0, child_collector, CT_strip_chart, true});
const MenuDef *menu_def = monitor->add_menu({CT_strip_chart, 0, child_collector, -1, true});
double value = child_level->get_net_value();
@ -787,7 +787,8 @@ menu_activate(GtkWidget *widget, gpointer data) {
case CT_flame_graph:
monitor->open_flame_graph(menu_def._thread_index,
menu_def._collector_index);
menu_def._collector_index,
menu_def._frame_number);
break;
case CT_piano_roll:
@ -853,7 +854,7 @@ handle_status_bar_popup(int item) {
const PStatViewLevel *child_level = view_level->get_child(c);
int child_collector = child_level->get_collector();
const MenuDef *menu_def = add_menu({0, child_collector, CT_strip_chart, true});
const MenuDef *menu_def = add_menu({CT_strip_chart, 0, child_collector, -1, true});
double value = child_level->get_net_value();

View File

@ -46,13 +46,14 @@ public:
class MenuDef {
public:
INLINE MenuDef(int thread_index, int collector_index,
ChartType chart_type, bool show_level = false);
INLINE MenuDef(ChartType chart_type, int thread_index, int collector_index,
int frame_number = -1, bool show_level = false);
INLINE bool operator < (const MenuDef &other) const;
ChartType _chart_type;
int _thread_index;
int _collector_index;
ChartType _chart_type;
int _frame_number;
bool _show_level;
GtkStatsMonitor *_monitor;
};
@ -84,7 +85,7 @@ public:
PStatGraph *open_timeline();
PStatGraph *open_strip_chart(int thread_index, int collector_index, bool show_level);
PStatGraph *open_flame_graph(int thread_index, int collector_index = -1);
PStatGraph *open_flame_graph(int thread_index, int collector_index = -1, int frame_number = -1);
PStatGraph *open_piano_roll(int thread_index);
void choose_collector_color(int collector_index);

View File

@ -149,7 +149,7 @@ on_popup_label(int collector_index) {
{
const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
_thread_index, collector_index, GtkStatsMonitor::CT_strip_chart, false,
GtkStatsMonitor::CT_strip_chart, _thread_index, collector_index,
});
GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Strip Chart");
@ -161,7 +161,7 @@ on_popup_label(int collector_index) {
{
const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
_thread_index, collector_index, GtkStatsMonitor::CT_flame_graph,
GtkStatsMonitor::CT_flame_graph, _thread_index, collector_index,
});
GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Flame Graph");
@ -178,7 +178,7 @@ on_popup_label(int collector_index) {
{
const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
-1, collector_index, GtkStatsMonitor::CT_choose_color,
GtkStatsMonitor::CT_choose_color, -1, collector_index,
});
GtkWidget *menu_item = gtk_menu_item_new_with_label("Change Color...");
@ -190,7 +190,7 @@ on_popup_label(int collector_index) {
{
const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
-1, collector_index, GtkStatsMonitor::CT_reset_color,
GtkStatsMonitor::CT_reset_color, -1, collector_index,
});
GtkWidget *menu_item = gtk_menu_item_new_with_label("Reset Color");

View File

@ -243,8 +243,8 @@ on_popup_label(int collector_index) {
{
const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
_thread_index, collector_index,
GtkStatsMonitor::CT_strip_chart, get_view().get_show_level(),
GtkStatsMonitor::CT_strip_chart, _thread_index, collector_index, -1,
get_view().get_show_level(),
});
GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Strip Chart");
@ -256,7 +256,7 @@ on_popup_label(int collector_index) {
if (!get_view().get_show_level()) {
const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
_thread_index, collector_index, GtkStatsMonitor::CT_flame_graph,
GtkStatsMonitor::CT_flame_graph, _thread_index, collector_index,
});
GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Flame Graph");
@ -273,7 +273,7 @@ on_popup_label(int collector_index) {
{
const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
-1, collector_index, GtkStatsMonitor::CT_choose_color,
GtkStatsMonitor::CT_choose_color, -1, collector_index,
});
GtkWidget *menu_item = gtk_menu_item_new_with_label("Change Color...");
@ -285,7 +285,7 @@ on_popup_label(int collector_index) {
{
const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
-1, collector_index, GtkStatsMonitor::CT_reset_color,
GtkStatsMonitor::CT_reset_color, -1, collector_index,
});
GtkWidget *menu_item = gtk_menu_item_new_with_label("Reset Color");

View File

@ -409,8 +409,8 @@ handle_button_press(int graph_x, int graph_y, bool double_click, int button) {
{
const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
GtkStatsMonitor::CT_strip_chart,
bar._thread_index, bar._collector_index,
GtkStatsMonitor::CT_strip_chart, false,
});
GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Strip Chart");
@ -422,8 +422,8 @@ handle_button_press(int graph_x, int graph_y, bool double_click, int button) {
{
const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
bar._thread_index, bar._collector_index,
GtkStatsMonitor::CT_flame_graph,
bar._thread_index, bar._collector_index, bar._frame_number,
});
GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Flame Graph");
@ -435,7 +435,7 @@ handle_button_press(int graph_x, int graph_y, bool double_click, int button) {
{
const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
bar._thread_index, -1, GtkStatsMonitor::CT_piano_roll,
GtkStatsMonitor::CT_piano_roll, bar._thread_index, -1,
});
GtkWidget *menu_item = gtk_menu_item_new_with_label("Open Piano Roll");
@ -452,8 +452,7 @@ handle_button_press(int graph_x, int graph_y, bool double_click, int button) {
{
const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
-1, bar._collector_index,
GtkStatsMonitor::CT_choose_color,
GtkStatsMonitor::CT_choose_color, -1, bar._collector_index,
});
GtkWidget *menu_item = gtk_menu_item_new_with_label("Change Color...");
@ -465,8 +464,7 @@ handle_button_press(int graph_x, int graph_y, bool double_click, int button) {
{
const GtkStatsMonitor::MenuDef *menu_def = GtkStatsGraph::_monitor->add_menu({
-1, bar._collector_index,
GtkStatsMonitor::CT_reset_color,
GtkStatsMonitor::CT_reset_color, -1, bar._collector_index,
});
GtkWidget *menu_item = gtk_menu_item_new_with_label("Reset Color");

View File

@ -25,7 +25,7 @@
class MacStatsFlameGraph final : public PStatFlameGraph, public MacStatsGraph {
public:
MacStatsFlameGraph(MacStatsMonitor *monitor, int thread_index,
int collector_index=-1);
int collector_index=-1, int frame_number=-1);
virtual ~MacStatsFlameGraph();
virtual void new_collector(int collector_index);
@ -60,11 +60,14 @@ protected:
virtual std::string get_graph_tooltip(int mouse_x, int mouse_y) const;
virtual DragMode consider_drag_start(int graph_x, int graph_y);
virtual bool handle_key(int graph_x, int graph_y, bool pressed,
UniChar c, unsigned short key_code);
virtual void handle_button_press(int graph_x, int graph_y,
bool double_click, int button);
bool double_click, int button);
virtual void handle_button_release(int graph_x, int graph_y);
virtual void handle_motion(int graph_x, int graph_y);
virtual void handle_leave();
virtual void handle_wheel(int graph_x, int graph_y, double dx, double dy);
virtual void handle_draw_graph(CGContextRef ctx, NSRect rect);
virtual void handle_back();

View File

@ -29,8 +29,8 @@ static const int default_flame_graph_height = 250;
*/
MacStatsFlameGraph::
MacStatsFlameGraph(MacStatsMonitor *monitor, int thread_index,
int collector_index) :
PStatFlameGraph(monitor, thread_index, collector_index, 0, 0),
int collector_index, int frame_number) :
PStatFlameGraph(monitor, thread_index, collector_index, frame_number, 0, 0),
MacStatsGraph(monitor, [MacStatsFlameGraphViewController alloc])
{
// Used for popup menus.
@ -538,6 +538,43 @@ consider_drag_start(int graph_x, int graph_y) {
return DM_none;
}
/**
*
*/
bool MacStatsFlameGraph::
handle_key(int graph_x, int graph_y, bool pressed, UniChar c, unsigned short key_code) {
bool changed = false;
if (pressed) {
switch (c) {
case NSLeftArrowFunctionKey:
changed = prev_frame();
break;
case NSRightArrowFunctionKey:
changed = next_frame();
break;
case NSHomeFunctionKey:
changed = first_frame();
break;
case NSEndFunctionKey:
changed = last_frame();
break;
}
}
if (changed) {
std::string window_title = get_title_text();
if (!is_title_unknown()) {
_window.title = [NSString stringWithUTF8String:window_title.c_str()];
}
}
return changed;
}
/**
* Called when the mouse button is depressed within the graph window.
*/
@ -652,6 +689,21 @@ handle_leave() {
return;
}
/**
*
*/
void MacStatsFlameGraph::
handle_wheel(int graph_x, int graph_y, double dx, double dy) {
if (dx != 0.0) {
if ((dx > 0.0) ? prev_frame() : next_frame()) {
std::string window_title = get_title_text();
if (!is_title_unknown()) {
_window.title = [NSString stringWithUTF8String:window_title.c_str()];
}
}
}
}
/**
* Fills in the graph window.
*/

View File

@ -45,15 +45,13 @@
}
- (void)keyDown:(NSEvent *)event {
if (!event.isARepeat) {
NSPoint pos = [self convertPoint:event.locationInWindow fromView:nil];
UniChar c = 0;
NSString *str = [event charactersIgnoringModifiers];
if (str != nil && str.length == 1) {
c = [str characterAtIndex:0];
}
_graph->handle_key(pos.x, pos.y, true, c, event.keyCode);
NSPoint pos = [self convertPoint:event.locationInWindow fromView:nil];
UniChar c = 0;
NSString *str = [event charactersIgnoringModifiers];
if (str != nil && str.length == 1) {
c = [str characterAtIndex:0];
}
_graph->handle_key(pos.x, pos.y, true, c, event.keyCode);
}
- (void)keyUp:(NSEvent *)event {

View File

@ -57,7 +57,7 @@ public:
PStatGraph *open_timeline();
PStatGraph *open_strip_chart(int thread_index, int collector_index, bool show_level);
PStatGraph *open_flame_graph(int thread_index, int collector_index = -1);
PStatGraph *open_flame_graph(int thread_index, int collector_index = -1, int frame_number = -1);
PStatGraph *open_piano_roll(int thread_index);
CGColorRef get_collector_color(int collector_index, bool highlight = false);

View File

@ -312,9 +312,9 @@ open_timeline() {
* Opens a new flame graph showing the indicated data.
*/
PStatGraph *MacStatsMonitor::
open_flame_graph(int thread_index, int collector_index) {
open_flame_graph(int thread_index, int collector_index, int frame_number) {
MacStatsFlameGraph *graph =
new MacStatsFlameGraph(this, thread_index, collector_index);
new MacStatsFlameGraph(this, thread_index, collector_index, frame_number);
add_graph(graph);
return graph;
}

View File

@ -819,7 +819,7 @@ handle_open_strip_chart() {
void MacStatsTimeline::
handle_open_flame_graph() {
const ColorBar &bar = _popup_bar;
MacStatsGraph::_monitor->open_flame_graph(bar._thread_index, bar._collector_index);
MacStatsGraph::_monitor->open_flame_graph(bar._thread_index, bar._collector_index, bar._frame_number);
}
/**

View File

@ -27,6 +27,15 @@ get_collector_index() const {
return _collector_index;
}
/**
* Returns the particular frame number whose data this flame graph reflects.
* Returns -1 if we're looking at a moving average instead.
*/
INLINE int PStatFlameGraph::
get_frame_number() const {
return _frame_number;
}
/**
* Returns the amount of total time the width of the horizontal axis
* represents.

View File

@ -25,12 +25,13 @@
*
*/
PStatFlameGraph::
PStatFlameGraph(PStatMonitor *monitor,
int thread_index, int collector_index, int xsize, int ysize) :
PStatFlameGraph(PStatMonitor *monitor, int thread_index, int collector_index,
int frame_number, int xsize, int ysize) :
PStatGraph(monitor, xsize, ysize),
_thread_index(thread_index),
_collector_index(collector_index),
_orig_collector_index(collector_index)
_orig_collector_index(collector_index),
_frame_number(frame_number)
{
_average_mode = true;
_average_cursor = 0;
@ -73,11 +74,20 @@ update() {
const PStatThreadData *thread_data =
client_data->get_thread_data(_thread_index);
if (!thread_data->is_empty()) {
int frame_number = thread_data->get_latest_frame_number();
if (frame_number != _current_frame) {
_current_frame = frame_number;
if (_frame_number >= 0) {
if (thread_data->has_frame(_frame_number)) {
if (_current_frame != _frame_number) {
_current_frame = _frame_number;
update_data();
}
}
} else {
int frame_number = thread_data->get_latest_frame_number();
if (frame_number != _current_frame) {
_current_frame = frame_number;
update_data();
update_data();
}
}
}
}
@ -117,6 +127,142 @@ set_collector_index(int collector_index) {
}
}
/**
* Changes the frame number shown by this flame graph. This may force a redraw.
*/
void PStatFlameGraph::
set_frame_number(int frame_number) {
if (_frame_number != frame_number) {
_frame_number = frame_number;
_current_frame = frame_number;
_title_unknown = true;
_stack.clear();
update_data();
if (_average_mode) {
_stack.update_averages(_average_cursor);
_time_width = _stack.get_net_value(true);
if (_time_width == 0.0) {
_time_width = 1.0 / pstats_target_frame_rate;
}
normal_guide_bars();
}
}
}
/**
* Sets the frame number to the oldest available frame.
*/
bool PStatFlameGraph::
first_frame() {
const PStatClientData *client_data = _monitor->get_client_data();
if (client_data == nullptr) {
return false;
}
const PStatThreadData *thread_data = client_data->get_thread_data(_thread_index);
if (thread_data == nullptr || thread_data->is_empty()) {
return false;
}
int oldest = thread_data->get_oldest_frame_number();
if (_frame_number != oldest && thread_data->has_frame(oldest)) {
set_frame_number(oldest);
return true;
}
return false;
}
/**
* Advances to the next available frame. Returns true if the frame number was
* changed after a call to this method.
*/
bool PStatFlameGraph::
next_frame() {
const PStatClientData *client_data = _monitor->get_client_data();
if (client_data == nullptr) {
return false;
}
const PStatThreadData *thread_data = client_data->get_thread_data(_thread_index);
if (thread_data == nullptr || thread_data->is_empty()) {
return false;
}
int latest = thread_data->get_latest_frame_number();
if (_frame_number < 0 || _frame_number > latest) {
set_frame_number(latest);
return true;
}
for (int i = _frame_number + 1; i <= latest; ++i) {
if (thread_data->has_frame(i)) {
set_frame_number(i);
return true;
}
}
return false;
}
/**
* Reverts to the previous frame. Returns true if the frame number was changed
* after a call to this method.
*/
bool PStatFlameGraph::
prev_frame() {
const PStatClientData *client_data = _monitor->get_client_data();
if (client_data == nullptr) {
return false;
}
const PStatThreadData *thread_data = client_data->get_thread_data(_thread_index);
if (thread_data == nullptr || thread_data->is_empty()) {
return false;
}
int oldest = thread_data->get_oldest_frame_number();
int i;
if (_frame_number < 0) {
i = thread_data->get_latest_frame_number();
} else {
i = _frame_number - 1;
}
while (i >= oldest) {
if (thread_data->has_frame(i)) {
set_frame_number(i);
return true;
}
--i;
}
return false;
}
/**
* Sets the frame number to the latest available frame.
*/
bool PStatFlameGraph::
last_frame() {
const PStatClientData *client_data = _monitor->get_client_data();
if (client_data == nullptr) {
return false;
}
const PStatThreadData *thread_data = client_data->get_thread_data(_thread_index);
if (thread_data == nullptr || thread_data->is_empty()) {
return false;
}
int latest = thread_data->get_latest_frame_number();
if (_frame_number != latest && thread_data->has_frame(latest)) {
set_frame_number(latest);
return true;
}
return false;
}
/**
* Returns the text suitable for the title label on the top line.
*/
@ -150,6 +296,10 @@ get_title_text() {
_title_unknown = true;
}
if (_frame_number >= 0) {
text += " (frame " + format_string(_frame_number) + ")";
}
return text;
}

View File

@ -36,7 +36,7 @@ class PStatFrameData;
class PStatFlameGraph : public PStatGraph {
public:
PStatFlameGraph(PStatMonitor *monitor,
int thread_index, int collector_index,
int thread_index, int collector_index, int frame_number,
int xsize, int ysize);
virtual ~PStatFlameGraph();
@ -46,6 +46,13 @@ public:
INLINE int get_collector_index() const;
void set_collector_index(int collector_index);
INLINE int get_frame_number() const;
void set_frame_number(int collector_index);
bool first_frame();
bool next_frame();
bool prev_frame();
bool last_frame();
INLINE double get_horizontal_scale() const;
INLINE void set_average_mode(bool average_mode);
@ -123,6 +130,7 @@ private:
int _thread_index;
int _collector_index;
int _orig_collector_index;
int _frame_number;
bool _average_mode;
size_t _average_cursor;

View File

@ -654,7 +654,7 @@ open_strip_chart(int thread_index, int collector_index, bool show_level) {
* Opens a new flame graph showing the indicated data.
*/
PStatGraph *PStatMonitor::
open_flame_graph(int thread_index, int collector_index) {
open_flame_graph(int thread_index, int collector_index, int frame_number) {
return nullptr;
}

View File

@ -104,7 +104,7 @@ public:
virtual PStatGraph *open_timeline();
virtual PStatGraph *open_strip_chart(int thread_index, int collector_index, bool show_level);
virtual PStatGraph *open_flame_graph(int thread_index, int collector_index = -1);
virtual PStatGraph *open_flame_graph(int thread_index, int collector_index = -1, int frame_number = -1);
virtual PStatGraph *open_piano_roll(int thread_index);
void write_datagram(Datagram &dg) const;

View File

@ -29,9 +29,9 @@ const char * const WinStatsFlameGraph::_window_class_name = "flame";
*/
WinStatsFlameGraph::
WinStatsFlameGraph(WinStatsMonitor *monitor, int thread_index,
int collector_index) :
int collector_index, int frame_number) :
PStatFlameGraph(monitor,
thread_index, collector_index,
thread_index, collector_index, frame_number,
monitor->get_pixel_scale() * default_flame_graph_width / 4,
monitor->get_pixel_scale() * default_flame_graph_height / 4),
WinStatsGraph(monitor)
@ -349,6 +349,7 @@ LONG WinStatsFlameGraph::
window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
switch (msg) {
case WM_LBUTTONDOWN:
SetFocus(_window);
if (_potential_drag_mode == DM_new_guide_bar) {
set_drag_mode(DM_new_guide_bar);
SetCapture(_graph_window);
@ -393,6 +394,31 @@ window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
}
break;
case WM_KEYDOWN:
{
bool changed = false;
switch (wparam) {
case VK_LEFT:
changed = prev_frame();
break;
case VK_RIGHT:
changed = next_frame();
break;
case VK_HOME:
changed = first_frame();
break;
case VK_END:
changed = last_frame();
break;
}
if (changed) {
std::string window_title = get_title_text();
SetWindowText(_window, window_title.c_str());
return 0;
}
}
break;
default:
break;
}
@ -407,6 +433,7 @@ LONG WinStatsFlameGraph::
graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
switch (msg) {
case WM_LBUTTONDOWN:
SetFocus(_window);
if (_potential_drag_mode == DM_guide_bar && _drag_guide_bar >= 0) {
set_drag_mode(DM_guide_bar);
int16_t x = LOWORD(lparam);
@ -518,6 +545,19 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
}
break;
case WM_MOUSEHWHEEL:
{
int delta = GET_WHEEL_DELTA_WPARAM(wparam);
if (delta != 0) {
if (delta > 0 ? next_frame() : prev_frame()) {
std::string window_title = get_title_text();
SetWindowText(_window, window_title.c_str());
}
}
return 0;
}
break;
default:
break;
}
@ -760,6 +800,8 @@ create_window() {
// Ensure that the window is on top of the stack.
SetWindowPos(_window, HWND_TOP, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
SetFocus(_window);
}
/**

View File

@ -28,7 +28,7 @@ class WinStatsLabel;
class WinStatsFlameGraph : public PStatFlameGraph, public WinStatsGraph {
public:
WinStatsFlameGraph(WinStatsMonitor *monitor, int thread_index,
int collector_index=-1);
int collector_index=-1, int frame_number=-1);
virtual ~WinStatsFlameGraph();
virtual void new_data(int thread_index, int frame_number);

View File

@ -332,8 +332,8 @@ open_strip_chart(int thread_index, int collector_index, bool show_level) {
* Opens a new flame graph showing the indicated data.
*/
PStatGraph *WinStatsMonitor::
open_flame_graph(int thread_index, int collector_index) {
WinStatsFlameGraph *graph = new WinStatsFlameGraph(this, thread_index, collector_index);
open_flame_graph(int thread_index, int collector_index, int frame_number) {
WinStatsFlameGraph *graph = new WinStatsFlameGraph(this, thread_index, collector_index, frame_number);
add_graph(graph);
return graph;
}

View File

@ -84,7 +84,7 @@ public:
PStatGraph *open_timeline();
PStatGraph *open_strip_chart(int thread_index, int collector_index, bool show_level);
PStatGraph *open_flame_graph(int thread_index, int collector_index = -1);
PStatGraph *open_flame_graph(int thread_index, int collector_index = -1, int frame_number = -1);
PStatGraph *open_piano_roll(int thread_index);
void choose_collector_color(int collector_index);

View File

@ -463,7 +463,7 @@ graph_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
return 0;
case 103:
WinStatsGraph::_monitor->open_flame_graph(_popup_bar._thread_index, _popup_bar._collector_index);
WinStatsGraph::_monitor->open_flame_graph(_popup_bar._thread_index, _popup_bar._collector_index, _popup_bar._frame_number);
return 0;
case 104: