From e8528609ece584e5d3ce3274119044a5110939ee Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Mon, 24 Jun 2019 20:28:18 -0700 Subject: [PATCH] libsysprof-ui: land new UI design This comprises a massive rewrite of the UI for browsing captures. We use the SysprofAid class to scan capture files for content and then auto- matically add visualizers and details pages. To avoid breaking things incrementally, we just land this as a very large commit. Not necessarily ideal, but given the amount of stuff that could break, this is easier. As part of this process, we're removing a lot of the surface API so that we can limit how much we need to maintain in terms of ABI. --- .../css/SysprofDisplay-shared.css | 89 ++ .../SysprofVisualizerView-Adwaita-dark.css | 6 - .../css/SysprofVisualizerView-Adwaita.css | 7 - .../css/SysprofVisualizerView-shared.css | 13 - src/libsysprof-ui/libsysprof-ui.gresource.xml | 16 +- src/libsysprof-ui/meson.build | 87 +- src/libsysprof-ui/rectangles.c | 22 +- src/libsysprof-ui/sysprof-aid.c | 63 +- src/libsysprof-ui/sysprof-aid.h | 62 +- src/libsysprof-ui/sysprof-battery-aid.c | 239 +++ ...visualizer-row.h => sysprof-battery-aid.h} | 10 +- src/libsysprof-ui/sysprof-callgraph-aid.c | 177 +++ src/libsysprof-ui/sysprof-callgraph-aid.h | 2 - src/libsysprof-ui/sysprof-callgraph-page.c | 1293 +++++++++++++++++ src/libsysprof-ui/sysprof-callgraph-page.h | 51 + src/libsysprof-ui/sysprof-callgraph-page.ui | 235 +++ src/libsysprof-ui/sysprof-callgraph-view.h | 12 - src/libsysprof-ui/sysprof-capture-view.c | 1092 -------------- src/libsysprof-ui/sysprof-capture-view.h | 75 - src/libsysprof-ui/sysprof-capture-view.ui | 187 --- .../sysprof-cell-renderer-duration.c | 2 +- .../sysprof-cell-renderer-percent.c | 2 + .../sysprof-cell-renderer-percent.h | 13 +- src/libsysprof-ui/sysprof-color-cycle.c | 65 +- src/libsysprof-ui/sysprof-color-cycle.h | 14 +- src/libsysprof-ui/sysprof-counters-aid.c | 248 ++++ src/libsysprof-ui/sysprof-counters-aid.h | 33 + src/libsysprof-ui/sysprof-cpu-aid.c | 249 ++++ src/libsysprof-ui/sysprof-cpu-aid.h | 2 - .../sysprof-cpu-visualizer-row.c | 239 --- .../sysprof-depth-visualizer-row.c | 357 ----- src/libsysprof-ui/sysprof-depth-visualizer.c | 430 ++++++ src/libsysprof-ui/sysprof-depth-visualizer.h | 40 + src/libsysprof-ui/sysprof-details-page.c | 324 +++++ src/libsysprof-ui/sysprof-details-page.h | 60 + src/libsysprof-ui/sysprof-details-page.ui | 372 +++++ src/libsysprof-ui/sysprof-display.c | 992 ++++++++++--- src/libsysprof-ui/sysprof-display.h | 55 +- src/libsysprof-ui/sysprof-display.ui | 40 +- src/libsysprof-ui/sysprof-empty-state-view.h | 6 - src/libsysprof-ui/sysprof-failed-state-view.h | 7 - .../sysprof-line-visualizer-row.h | 70 - ...alizer-row.c => sysprof-line-visualizer.c} | 313 ++-- src/libsysprof-ui/sysprof-line-visualizer.h | 57 + src/libsysprof-ui/sysprof-logs-aid.c | 237 +++ src/libsysprof-ui/sysprof-logs-aid.h | 33 + src/libsysprof-ui/sysprof-logs-page.c | 116 ++ ...izer-row-private.h => sysprof-logs-page.h} | 10 +- src/libsysprof-ui/sysprof-logs-page.ui | 75 + .../sysprof-mark-visualizer-row.c | 492 ------- .../sysprof-mark-visualizer-row.h | 53 - src/libsysprof-ui/sysprof-mark-visualizer.c | 287 ++++ src/libsysprof-ui/sysprof-mark-visualizer.h | 47 + src/libsysprof-ui/sysprof-marks-aid.c | 319 ++++ src/libsysprof-ui/sysprof-marks-aid.h | 33 + ...prof-marks-view.c => sysprof-marks-page.c} | 437 +++--- ...-visualizer-row.h => sysprof-marks-page.h} | 31 +- src/libsysprof-ui/sysprof-marks-page.ui | 231 +++ src/libsysprof-ui/sysprof-marks-view.h | 55 - src/libsysprof-ui/sysprof-marks-view.ui | 57 - src/libsysprof-ui/sysprof-memory-aid.h | 2 - src/libsysprof-ui/sysprof-model-filter.h | 2 +- src/libsysprof-ui/sysprof-notebook.h | 4 + src/libsysprof-ui/sysprof-page.c | 235 +++ src/libsysprof-ui/sysprof-page.h | 86 ++ .../sysprof-profiler-assistant.c | 11 +- .../sysprof-profiler-assistant.h | 2 - .../sysprof-profiler-assistant.ui | 8 +- src/libsysprof-ui/sysprof-proxy-aid.h | 3 - .../sysprof-recording-state-view.h | 7 - src/libsysprof-ui/sysprof-scrollmap.c | 306 ++++ src/libsysprof-ui/sysprof-scrollmap.h | 37 + src/libsysprof-ui/sysprof-theme-manager.c | 6 +- src/libsysprof-ui/sysprof-time-visualizer.c | 543 +++++++ src/libsysprof-ui/sysprof-time-visualizer.h | 54 + src/libsysprof-ui/sysprof-ui-private.h | 38 +- src/libsysprof-ui/sysprof-ui.h | 23 +- .../sysprof-visualizer-group-header.c | 159 ++ .../sysprof-visualizer-group-header.h | 31 + .../sysprof-visualizer-group-private.h | 48 + src/libsysprof-ui/sysprof-visualizer-group.c | 511 +++++++ src/libsysprof-ui/sysprof-visualizer-group.h | 76 + src/libsysprof-ui/sysprof-visualizer-list.c | 582 -------- src/libsysprof-ui/sysprof-visualizer-list.h | 60 - src/libsysprof-ui/sysprof-visualizer-row.c | 300 ---- src/libsysprof-ui/sysprof-visualizer-row.h | 83 -- src/libsysprof-ui/sysprof-visualizer-ticks.c | 81 +- src/libsysprof-ui/sysprof-visualizer-ticks.h | 5 +- src/libsysprof-ui/sysprof-visualizer-view.c | 762 ---------- src/libsysprof-ui/sysprof-visualizer-view.h | 70 - src/libsysprof-ui/sysprof-visualizer-view.ui | 32 - src/libsysprof-ui/sysprof-visualizer.c | 346 +++++ src/libsysprof-ui/sysprof-visualizer.h | 82 ++ src/libsysprof-ui/sysprof-visualizers-frame.c | 767 ++++++++++ src/libsysprof-ui/sysprof-visualizers-frame.h | 49 + .../sysprof-visualizers-frame.ui | 419 ++++++ src/libsysprof-ui/sysprof-zoom-manager.h | 27 +- src/tests/meson.build | 3 +- src/tests/test-capture-view.c | 8 +- 99 files changed, 10571 insertions(+), 5538 deletions(-) create mode 100644 src/libsysprof-ui/css/SysprofDisplay-shared.css delete mode 100644 src/libsysprof-ui/css/SysprofVisualizerView-Adwaita-dark.css delete mode 100644 src/libsysprof-ui/css/SysprofVisualizerView-Adwaita.css delete mode 100644 src/libsysprof-ui/css/SysprofVisualizerView-shared.css create mode 100644 src/libsysprof-ui/sysprof-battery-aid.c rename src/libsysprof-ui/{sysprof-depth-visualizer-row.h => sysprof-battery-aid.h} (73%) create mode 100644 src/libsysprof-ui/sysprof-callgraph-page.c create mode 100644 src/libsysprof-ui/sysprof-callgraph-page.h create mode 100644 src/libsysprof-ui/sysprof-callgraph-page.ui delete mode 100644 src/libsysprof-ui/sysprof-capture-view.c delete mode 100644 src/libsysprof-ui/sysprof-capture-view.h delete mode 100644 src/libsysprof-ui/sysprof-capture-view.ui create mode 100644 src/libsysprof-ui/sysprof-counters-aid.c create mode 100644 src/libsysprof-ui/sysprof-counters-aid.h delete mode 100644 src/libsysprof-ui/sysprof-cpu-visualizer-row.c delete mode 100644 src/libsysprof-ui/sysprof-depth-visualizer-row.c create mode 100644 src/libsysprof-ui/sysprof-depth-visualizer.c create mode 100644 src/libsysprof-ui/sysprof-depth-visualizer.h create mode 100644 src/libsysprof-ui/sysprof-details-page.c create mode 100644 src/libsysprof-ui/sysprof-details-page.h create mode 100644 src/libsysprof-ui/sysprof-details-page.ui delete mode 100644 src/libsysprof-ui/sysprof-line-visualizer-row.h rename src/libsysprof-ui/{sysprof-line-visualizer-row.c => sysprof-line-visualizer.c} (62%) create mode 100644 src/libsysprof-ui/sysprof-line-visualizer.h create mode 100644 src/libsysprof-ui/sysprof-logs-aid.c create mode 100644 src/libsysprof-ui/sysprof-logs-aid.h create mode 100644 src/libsysprof-ui/sysprof-logs-page.c rename src/libsysprof-ui/{sysprof-visualizer-row-private.h => sysprof-logs-page.h} (74%) create mode 100644 src/libsysprof-ui/sysprof-logs-page.ui delete mode 100644 src/libsysprof-ui/sysprof-mark-visualizer-row.c delete mode 100644 src/libsysprof-ui/sysprof-mark-visualizer-row.h create mode 100644 src/libsysprof-ui/sysprof-mark-visualizer.c create mode 100644 src/libsysprof-ui/sysprof-mark-visualizer.h create mode 100644 src/libsysprof-ui/sysprof-marks-aid.c create mode 100644 src/libsysprof-ui/sysprof-marks-aid.h rename src/libsysprof-ui/{sysprof-marks-view.c => sysprof-marks-page.c} (64%) rename src/libsysprof-ui/{sysprof-cpu-visualizer-row.h => sysprof-marks-page.h} (55%) create mode 100644 src/libsysprof-ui/sysprof-marks-page.ui delete mode 100644 src/libsysprof-ui/sysprof-marks-view.h delete mode 100644 src/libsysprof-ui/sysprof-marks-view.ui create mode 100644 src/libsysprof-ui/sysprof-page.c create mode 100644 src/libsysprof-ui/sysprof-page.h create mode 100644 src/libsysprof-ui/sysprof-scrollmap.c create mode 100644 src/libsysprof-ui/sysprof-scrollmap.h create mode 100644 src/libsysprof-ui/sysprof-time-visualizer.c create mode 100644 src/libsysprof-ui/sysprof-time-visualizer.h create mode 100644 src/libsysprof-ui/sysprof-visualizer-group-header.c create mode 100644 src/libsysprof-ui/sysprof-visualizer-group-header.h create mode 100644 src/libsysprof-ui/sysprof-visualizer-group-private.h create mode 100644 src/libsysprof-ui/sysprof-visualizer-group.c create mode 100644 src/libsysprof-ui/sysprof-visualizer-group.h delete mode 100644 src/libsysprof-ui/sysprof-visualizer-list.c delete mode 100644 src/libsysprof-ui/sysprof-visualizer-list.h delete mode 100644 src/libsysprof-ui/sysprof-visualizer-row.c delete mode 100644 src/libsysprof-ui/sysprof-visualizer-row.h delete mode 100644 src/libsysprof-ui/sysprof-visualizer-view.c delete mode 100644 src/libsysprof-ui/sysprof-visualizer-view.h delete mode 100644 src/libsysprof-ui/sysprof-visualizer-view.ui create mode 100644 src/libsysprof-ui/sysprof-visualizer.c create mode 100644 src/libsysprof-ui/sysprof-visualizer.h create mode 100644 src/libsysprof-ui/sysprof-visualizers-frame.c create mode 100644 src/libsysprof-ui/sysprof-visualizers-frame.h create mode 100644 src/libsysprof-ui/sysprof-visualizers-frame.ui diff --git a/src/libsysprof-ui/css/SysprofDisplay-shared.css b/src/libsysprof-ui/css/SysprofDisplay-shared.css new file mode 100644 index 00000000..45eddf02 --- /dev/null +++ b/src/libsysprof-ui/css/SysprofDisplay-shared.css @@ -0,0 +1,89 @@ +SysprofDisplay dzlmultipaned { + -DzlMultiPaned-handle-size: 0; + } + +SysprofVisualizer { + background: @content_view_bg; + } +SysprofVisualizer:not(:last-child) { + border-bottom: 1px solid alpha(@borders, 0.3); + } + +SysprofVisualizerGroup { + border-bottom: 1px solid @borders; + } +SysprofVisualizerGroup:last-child { + box-shadow: 0 20px 15px 15px alpha(@borders, 0.3); + } + +SysprofVisualizersFrame box.horizontal.inline-toolbar { + padding: 0; + margin: 0; + border: none; + border-radius: 0; + border-width: 0; + } + +SysprofVisualizersFrame scrollbar.horizontal { + color: mix(@theme_fg_color, @theme_selected_bg_color, 0.5); + background: transparent; + } + +SysprofVisualizersFrame scrollbar.horizontal contents trough { + background: transparent; + } + +SysprofVisualizersFrame scrollbar.horizontal contents trough slider { + margin-left: 1px; + margin-right: 1px; + padding: 6px; + min-height: 14px; + background: alpha(@content_view_bg, 0.2); + border-radius: 3px; + border: 2px solid @theme_selected_bg_color; + box-shadow: inset 0 10px 5px alpha(@content_view_bg,.3), + inset 0 -15px 5px alpha(@content_view_bg,.1), + 0px 2px 4px @borders; + } + +SysprofVisualizerTicks { + color: mix(@theme_fg_color, @borders, 0.5); + background-color: @theme_bg_color; + } + +SysprofVisualizersFrame list { + background-color: @theme_bg_color; + } + +SysprofVisualizersFrame list.visualizer-groups row { + padding: 0; + border-bottom: 1px solid @borders; + } +SysprofVisualizersFrame list.visualizer-groups row:not(:selected) { + background-color: @theme_bg_color; + } +SysprofVisualizersFrame list.visualizer-groups row:last-child { + box-shadow: 0 20px 15px 15px alpha(@borders, 0.3); + } + +SysprofVisualizersFrame .left-column .small-button.flat { + border-color: transparent; + min-height: 8px; + min-width: 8px; + } +SysprofVisualizersFrame .left-column .small-button.flat:checked, +SysprofVisualizersFrame .left-column .small-button.flat:hover { + border-color: @borders; + } + +SysprofDisplay > dzlmultipaned > :nth-child(2) { + border-top: 1px solid @borders; + } + +SysprofVisualizersFrame .selection { + border-radius: 3px; + background-color: alpha(@theme_selected_bg_color, 0.35); + box-shadow: inset 0 10px 5px alpha(@content_view_bg,.3), + inset 0 -15px 5px alpha(@content_view_bg,.1), + inset 0 0 1px 1px @theme_selected_bg_color; + } diff --git a/src/libsysprof-ui/css/SysprofVisualizerView-Adwaita-dark.css b/src/libsysprof-ui/css/SysprofVisualizerView-Adwaita-dark.css deleted file mode 100644 index 28484537..00000000 --- a/src/libsysprof-ui/css/SysprofVisualizerView-Adwaita-dark.css +++ /dev/null @@ -1,6 +0,0 @@ -visualizers list row { - background-color: #201f21; - background-size: 8px 8px; - background-image: repeating-linear-gradient(0deg, #232224, #232224 1px, transparent 1px, transparent 8px), - repeating-linear-gradient(-90deg, #232224, #232224 1px, transparent 1px, transparent 8px); -} diff --git a/src/libsysprof-ui/css/SysprofVisualizerView-Adwaita.css b/src/libsysprof-ui/css/SysprofVisualizerView-Adwaita.css deleted file mode 100644 index 4a4f1a76..00000000 --- a/src/libsysprof-ui/css/SysprofVisualizerView-Adwaita.css +++ /dev/null @@ -1,7 +0,0 @@ -visualizers list row { - background-color: #f6f7f8; - background-size: 8px 8px; - background-image: repeating-linear-gradient(0deg, #f0f1f2, #f0f1f2 1px, transparent 1px, transparent 8px), - repeating-linear-gradient(-90deg, #f0f1f2, #f0f1f2 1px, transparent 1px, transparent 8px); -} - diff --git a/src/libsysprof-ui/css/SysprofVisualizerView-shared.css b/src/libsysprof-ui/css/SysprofVisualizerView-shared.css deleted file mode 100644 index bc0fca45..00000000 --- a/src/libsysprof-ui/css/SysprofVisualizerView-shared.css +++ /dev/null @@ -1,13 +0,0 @@ -visualizers.selection { - background: none; - background-color: alpha(@theme_selected_bg_color, 0.35); - box-shadow: 0 0 0 1px @theme_selected_bg_color inset; -} -visualizers.selection:backdrop { - background: none; - background-color: alpha(@theme_selected_bg_color, 0.15); - border: none; -} -visualizers list row:not(:first-child) { - border-top: 1px solid alpha(@borders, 0.4); -} diff --git a/src/libsysprof-ui/libsysprof-ui.gresource.xml b/src/libsysprof-ui/libsysprof-ui.gresource.xml index a4bca358..fd6a5978 100644 --- a/src/libsysprof-ui/libsysprof-ui.gresource.xml +++ b/src/libsysprof-ui/libsysprof-ui.gresource.xml @@ -2,9 +2,7 @@ css/SysprofEnvironEditor-shared.css - css/SysprofVisualizerView-shared.css - css/SysprofVisualizerView-Adwaita.css - css/SysprofVisualizerView-Adwaita-dark.css + css/SysprofDisplay-shared.css ../../data/icons/scalable/apps/org.gnome.Sysprof.svg @@ -14,20 +12,18 @@ sysprof-aid-icon.ui - sysprof-callgraph-view.ui - sysprof-capture-view.ui + sysprof-callgraph-page.ui + sysprof-details-page.ui sysprof-display.ui - sysprof-details-view.ui - sysprof-empty-state-view.ui sysprof-environ-editor-row.ui sysprof-failed-state-view.ui - sysprof-logs-view.ui - sysprof-marks-view.ui + sysprof-logs-page.ui + sysprof-marks-page.ui sysprof-process-model-row.ui sysprof-profiler-assistant.ui sysprof-recording-state-view.ui sysprof-tab.ui sysprof-time-label.ui - sysprof-visualizer-view.ui + sysprof-visualizers-frame.ui diff --git a/src/libsysprof-ui/meson.build b/src/libsysprof-ui/meson.build index 5a919ac5..69ec5799 100644 --- a/src/libsysprof-ui/meson.build +++ b/src/libsysprof-ui/meson.build @@ -1,84 +1,71 @@ if get_option('enable_gtk') and get_option('libsysprof') libsysprof_ui_public_sources = [ - 'sysprof-aid.c', - 'sysprof-capture-view.c', - 'sysprof-callgraph-aid.c', - 'sysprof-callgraph-view.c', 'sysprof-check.c', - 'sysprof-color-cycle.c', - 'sysprof-cpu-aid.c', - 'sysprof-cpu-visualizer-row.c', 'sysprof-display.c', - 'sysprof-empty-state-view.c', - 'sysprof-failed-state-view.c', - 'sysprof-line-visualizer-row.c', - 'sysprof-marks-model.c', - 'sysprof-marks-view.c', - 'sysprof-mark-visualizer-row.c', - 'sysprof-memory-aid.c', 'sysprof-model-filter.c', 'sysprof-notebook.c', + 'sysprof-page.c', 'sysprof-process-model-row.c', - 'sysprof-proxy-aid.c', - 'sysprof-profiler-assistant.c', - 'sysprof-recording-state-view.c', - 'sysprof-visualizer-list.c', - 'sysprof-visualizer-row.c', - 'sysprof-visualizer-ticks.c', - 'sysprof-visualizer-view.c', + 'sysprof-visualizer.c', + 'sysprof-visualizer-group.c', 'sysprof-zoom-manager.c', ] libsysprof_ui_private_sources = [ 'pointcache.c', 'rectangles.c', + 'sysprof-aid.c', 'sysprof-aid-icon.c', - 'sysprof-details-view.c', + 'sysprof-battery-aid.c', 'sysprof-cairo.c', + 'sysprof-callgraph-aid.c', + 'sysprof-callgraph-page.c', 'sysprof-cell-renderer-duration.c', 'sysprof-cell-renderer-percent.c', - 'sysprof-depth-visualizer-row.c', - 'sysprof-environ-editor-row.c', - 'sysprof-environ-editor.c', - 'sysprof-environ-variable.c', + 'sysprof-color-cycle.c', + 'sysprof-counters-aid.c', + 'sysprof-cpu-aid.c', + 'sysprof-depth-visualizer.c', + 'sysprof-details-page.c', + 'sysprof-display.c', 'sysprof-environ.c', + 'sysprof-environ-editor.c', + 'sysprof-environ-editor-row.c', + 'sysprof-environ-variable.c', + 'sysprof-failed-state-view.c', + 'sysprof-line-visualizer.c', 'sysprof-log-model.c', - 'sysprof-logs-view.c', + 'sysprof-logs-aid.c', + 'sysprof-logs-page.c', + 'sysprof-marks-aid.c', + 'sysprof-marks-model.c', + 'sysprof-marks-page.c', + 'sysprof-mark-visualizer.c', + 'sysprof-memory-aid.c', + 'sysprof-profiler-assistant.c', + 'sysprof-proxy-aid.c', + 'sysprof-recording-state-view.c', + 'sysprof-scrollmap.c', 'sysprof-tab.c', - 'sysprof-time-label.c', 'sysprof-theme-manager.c', + 'sysprof-time-label.c', + 'sysprof-time-visualizer.c', + 'sysprof-visualizer-group-header.c', + 'sysprof-visualizers-frame.c', + 'sysprof-visualizer-ticks.c', '../stackstash.c', ] libsysprof_ui_public_headers = [ - 'sysprof-aid.h', - 'sysprof-capture-view.h', - 'sysprof-callgraph-aid.h', - 'sysprof-callgraph-view.h', - 'sysprof-cell-renderer-percent.h', 'sysprof-check.h', - 'sysprof-cpu-aid.h', - 'sysprof-cpu-visualizer-row.h', 'sysprof-display.h', - 'sysprof-empty-state-view.h', - 'sysprof-failed-state-view.h', - 'sysprof-line-visualizer-row.h', - 'sysprof-marks-model.h', - 'sysprof-marks-view.h', - 'sysprof-mark-visualizer-row.h', - 'sysprof-memory-aid.h', - 'sysprof-model-filter.c', 'sysprof-model-filter.h', 'sysprof-notebook.h', + 'sysprof-page.h', 'sysprof-process-model-row.h', - 'sysprof-proxy-aid.h', - 'sysprof-profiler-assistant.h', - 'sysprof-recording-state-view.h', - 'sysprof-visualizer-list.h', - 'sysprof-visualizer-row.h', - 'sysprof-visualizer-ticks.h', - 'sysprof-visualizer-view.h', + 'sysprof-visualizer.h', + 'sysprof-visualizer-group.h', 'sysprof-zoom-manager.h', 'sysprof-ui.h', ] diff --git a/src/libsysprof-ui/rectangles.c b/src/libsysprof-ui/rectangles.c index 16235029..2ac8ca05 100644 --- a/src/libsysprof-ui/rectangles.c +++ b/src/libsysprof-ui/rectangles.c @@ -20,7 +20,7 @@ #include "rectangles.h" #include "sysprof-color-cycle.h" -#include "sysprof-visualizer-row.h" +#include "sysprof-visualizer.h" typedef struct { @@ -153,18 +153,18 @@ rectangles_draw (Rectangles *self, { GtkAllocation alloc; gdouble range; - guint n_rows; + guint ns; g_assert (self != NULL); - g_assert (SYSPROF_IS_VISUALIZER_ROW (row)); + g_assert (SYSPROF_IS_VISUALIZER (row)); g_assert (cr != NULL); if (!self->sorted) rectangles_sort (self); gtk_widget_get_allocation (row, &alloc); - n_rows = g_hash_table_size (self->y_indexes); - if (n_rows == 0 || alloc.height == 0) + ns = g_hash_table_size (self->y_indexes); + if (ns == 0 || alloc.height == 0) return; range = self->end_time - self->begin_time; @@ -173,24 +173,24 @@ rectangles_draw (Rectangles *self, { Rectangle *rect = &g_array_index (self->rectangles, Rectangle, i); guint y_index = GPOINTER_TO_UINT (g_hash_table_lookup (self->y_indexes, rect->name)); - SysprofVisualizerRowRelativePoint in_points[2]; - SysprofVisualizerRowAbsolutePoint out_points[2]; + SysprofVisualizerRelativePoint in_points[2]; + SysprofVisualizerAbsolutePoint out_points[2]; GdkRectangle r; GdkRGBA *rgba; g_assert (y_index > 0); - g_assert (y_index <= n_rows); + g_assert (y_index <= ns); in_points[0].x = (rect->begin - self->begin_time) / range; - in_points[0].y = (y_index - 1) / (gdouble)n_rows; + in_points[0].y = (y_index - 1) / (gdouble)ns; in_points[1].x = (rect->end - self->begin_time) / range; in_points[1].y = 0; - sysprof_visualizer_row_translate_points (SYSPROF_VISUALIZER_ROW (row), + sysprof_visualizer_translate_points (SYSPROF_VISUALIZER (row), in_points, G_N_ELEMENTS (in_points), out_points, G_N_ELEMENTS (out_points)); - r.height = alloc.height / (gdouble)n_rows; + r.height = alloc.height / (gdouble)ns; r.x = out_points[0].x; r.y = out_points[0].y - r.height; diff --git a/src/libsysprof-ui/sysprof-aid.c b/src/libsysprof-ui/sysprof-aid.c index c1461f2e..1bf3a489 100644 --- a/src/libsysprof-ui/sysprof-aid.c +++ b/src/libsysprof-ui/sysprof-aid.c @@ -49,6 +49,29 @@ enum { static GParamSpec *properties [N_PROPS]; +static void +sysprof_aid_real_present_async (SysprofAid *self, + SysprofCaptureReader *reader, + SysprofDisplay *display, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_task_report_new_error (self, callback, user_data, + sysprof_aid_real_present_async, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "Not supported"); +} + +static gboolean +sysprof_aid_real_present_finish (SysprofAid *self, + GAsyncResult *result, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (result), error); +} + static void sysprof_aid_finalize (GObject *object) { @@ -121,6 +144,9 @@ sysprof_aid_class_init (SysprofAidClass *klass) object_class->get_property = sysprof_aid_get_property; object_class->set_property = sysprof_aid_set_property; + klass->present_async = sysprof_aid_real_present_async; + klass->present_finish = sysprof_aid_real_present_finish; + properties [PROP_DISPLAY_NAME] = g_param_spec_string ("display-name", "Display Name", @@ -146,7 +172,7 @@ sysprof_aid_class_init (SysprofAidClass *klass) } static void -sysprof_aid_init (SysprofAid *self) +sysprof_aid_init (SysprofAid *self G_GNUC_UNUSED) { } @@ -258,10 +284,10 @@ sysprof_aid_prepare (SysprofAid *self, } static void -sysprof_aid_add_child (GtkBuildable *buildable, - GtkBuilder *builder, - GObject *object, - const gchar *type) +sysprof_aid_add_child (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *object, + const gchar *type G_GNUC_UNUSED) { SysprofAid *self = (SysprofAid *)buildable; SysprofAidPrivate *priv = sysprof_aid_get_instance_private (self); @@ -298,3 +324,30 @@ sysprof_aid_new (const gchar *display_name, "icon-name", icon_name, NULL); } + +void +sysprof_aid_present_async (SysprofAid *self, + SysprofCaptureReader *reader, + SysprofDisplay *display, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail (SYSPROF_IS_AID (self)); + g_return_if_fail (reader != NULL); + g_return_if_fail (SYSPROF_IS_DISPLAY (display)); + g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + + SYSPROF_AID_GET_CLASS (self)->present_async (self, reader, display, cancellable, callback, user_data); +} + +gboolean +sysprof_aid_present_finish (SysprofAid *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (SYSPROF_IS_AID (self), FALSE); + g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); + + return SYSPROF_AID_GET_CLASS (self)->present_finish (self, result, error); +} diff --git a/src/libsysprof-ui/sysprof-aid.h b/src/libsysprof-ui/sysprof-aid.h index ba8dfe3d..70a16667 100644 --- a/src/libsysprof-ui/sysprof-aid.h +++ b/src/libsysprof-ui/sysprof-aid.h @@ -20,49 +20,57 @@ #pragma once -#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION) -# error "Only can be included directly." -#endif - #include #include +#include "sysprof-display.h" + G_BEGIN_DECLS #define SYSPROF_TYPE_AID (sysprof_aid_get_type()) -SYSPROF_AVAILABLE_IN_ALL G_DECLARE_DERIVABLE_TYPE (SysprofAid, sysprof_aid, SYSPROF, AID, GObject) struct _SysprofAidClass { GObjectClass parent_class; - void (*prepare) (SysprofAid *self, - SysprofProfiler *profiler); + void (*prepare) (SysprofAid *self, + SysprofProfiler *profiler); + void (*present_async) (SysprofAid *self, + SysprofCaptureReader *reader, + SysprofDisplay *display, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean (*present_finish) (SysprofAid *self, + GAsyncResult *result, + GError **error); - /*< gpointer >*/ + /*< private >*/ gpointer _reserved[16]; }; -SYSPROF_AVAILABLE_IN_ALL -SysprofAid *sysprof_aid_new (const gchar *display_name, - const gchar *icon_name); -SYSPROF_AVAILABLE_IN_ALL -const gchar *sysprof_aid_get_display_name (SysprofAid *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_aid_set_display_name (SysprofAid *self, - const gchar *display_name); -SYSPROF_AVAILABLE_IN_ALL -GIcon *sysprof_aid_get_icon (SysprofAid *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_aid_set_icon (SysprofAid *self, - GIcon *icon); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_aid_set_icon_name (SysprofAid *self, - const gchar *icon_name); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_aid_prepare (SysprofAid *self, - SysprofProfiler *profiler); +SysprofAid *sysprof_aid_new (const gchar *display_name, + const gchar *icon_name); +const gchar *sysprof_aid_get_display_name (SysprofAid *self); +void sysprof_aid_set_display_name (SysprofAid *self, + const gchar *display_name); +GIcon *sysprof_aid_get_icon (SysprofAid *self); +void sysprof_aid_set_icon (SysprofAid *self, + GIcon *icon); +void sysprof_aid_set_icon_name (SysprofAid *self, + const gchar *icon_name); +void sysprof_aid_prepare (SysprofAid *self, + SysprofProfiler *profiler); +void sysprof_aid_present_async (SysprofAid *self, + SysprofCaptureReader *reader, + SysprofDisplay *display, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean sysprof_aid_present_finish (SysprofAid *self, + GAsyncResult *result, + GError **error); G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-battery-aid.c b/src/libsysprof-ui/sysprof-battery-aid.c new file mode 100644 index 00000000..5675030c --- /dev/null +++ b/src/libsysprof-ui/sysprof-battery-aid.c @@ -0,0 +1,239 @@ +/* sysprof-battery-aid.c + * + * Copyright 2019 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#define G_LOG_DOMAIN "sysprof-battery-aid" + +#include "config.h" + +#include + +#include "sysprof-color-cycle.h" +#include "sysprof-battery-aid.h" +#include "sysprof-line-visualizer.h" + +struct _SysprofBatteryAid +{ + SysprofAid parent_instance; +}; + +typedef struct +{ + SysprofCaptureCursor *cursor; + SysprofDisplay *display; +} Present; + +G_DEFINE_TYPE (SysprofBatteryAid, sysprof_battery_aid, SYSPROF_TYPE_AID) + +static void +present_free (gpointer data) +{ + Present *p = data; + + g_clear_pointer (&p->cursor, sysprof_capture_cursor_unref); + g_clear_object (&p->display); + g_slice_free (Present, p); +} + +/** + * sysprof_battery_aid_new: + * + * Create a new #SysprofBatteryAid. + * + * Returns: (transfer full): a newly created #SysprofBatteryAid + * + * Since: 3.34 + */ +SysprofAid * +sysprof_battery_aid_new (void) +{ + return g_object_new (SYSPROF_TYPE_BATTERY_AID, NULL); +} + +static void +sysprof_battery_aid_prepare (SysprofAid *self, + SysprofProfiler *profiler) +{ +#ifdef __linux__ + g_autoptr(SysprofSource) source = NULL; + + g_assert (SYSPROF_IS_BATTERY_AID (self)); + g_assert (SYSPROF_IS_PROFILER (profiler)); + + source = sysprof_battery_source_new (); + sysprof_profiler_add_source (profiler, source); +#endif +} + +static gboolean +collect_battery_counters (const SysprofCaptureFrame *frame, + gpointer user_data) +{ + SysprofCaptureCounterDefine *def = (SysprofCaptureCounterDefine *)frame; + GArray *counters = user_data; + + g_assert (frame != NULL); + g_assert (frame->type == SYSPROF_CAPTURE_FRAME_CTRDEF); + g_assert (counters != NULL); + + for (guint i = 0; i < def->n_counters; i++) + { + const SysprofCaptureCounter *counter = &def->counters[i]; + + if (g_strcmp0 (counter->category, "Battery Charge") == 0) + g_array_append_vals (counters, counter, 1); + } + + return TRUE; +} + +static void +sysprof_battery_aid_present_worker (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + Present *present = task_data; + g_autoptr(GArray) counters = NULL; + + g_assert (G_IS_TASK (task)); + g_assert (SYSPROF_IS_BATTERY_AID (source_object)); + g_assert (present != NULL); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + counters = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounter)); + sysprof_capture_cursor_foreach (present->cursor, collect_battery_counters, counters); + g_task_return_pointer (task, + g_steal_pointer (&counters), + (GDestroyNotify) g_array_unref); +} + +static void +sysprof_battery_aid_present_async (SysprofAid *aid, + SysprofCaptureReader *reader, + SysprofDisplay *display, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + static const SysprofCaptureFrameType types[] = { SYSPROF_CAPTURE_FRAME_CTRDEF }; + g_autoptr(SysprofCaptureCondition) condition = NULL; + g_autoptr(SysprofCaptureCursor) cursor = NULL; + g_autoptr(GTask) task = NULL; + Present present; + + g_assert (SYSPROF_IS_BATTERY_AID (aid)); + g_assert (reader != NULL); + g_assert (SYSPROF_IS_DISPLAY (display)); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + condition = sysprof_capture_condition_new_where_type_in (1, types); + cursor = sysprof_capture_cursor_new (reader); + sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition)); + + present.cursor = g_steal_pointer (&cursor); + present.display = g_object_ref (display); + + task = g_task_new (aid, cancellable, callback, user_data); + g_task_set_source_tag (task, sysprof_battery_aid_present_async); + g_task_set_task_data (task, + g_slice_dup (Present, &present), + present_free); + g_task_run_in_thread (task, sysprof_battery_aid_present_worker); +} + +static gboolean +sysprof_battery_aid_present_finish (SysprofAid *aid, + GAsyncResult *result, + GError **error) +{ + g_autoptr(GArray) counters = NULL; + Present *present; + + g_assert (SYSPROF_IS_AID (aid)); + g_assert (G_IS_TASK (result)); + + present = g_task_get_task_data (G_TASK (result)); + + if ((counters = g_task_propagate_pointer (G_TASK (result), error))) + { + g_autoptr(SysprofColorCycle) cycle = sysprof_color_cycle_new (); + SysprofVisualizerGroup *group; + + group = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP, + "can-focus", TRUE, + "title", _("Battery Charge"), + "visible", TRUE, + NULL); + + for (guint i = 0; i < counters->len; i++) + { + const SysprofCaptureCounter *ctr = &g_array_index (counters, SysprofCaptureCounter, i); + + if (g_strcmp0 (ctr->category, "Battery Charge") == 0) + { + g_autofree gchar *title = NULL; + gboolean is_combined = g_str_equal (ctr->name, "Combined"); + GtkWidget *row; + GdkRGBA rgba; + + if (is_combined) + title = g_strdup (_("Battery Charge (All)")); + else + title = g_strdup_printf ("Battery Charge (%s)", ctr->name); + + row = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER, + "title", title, + "height-request", 35, + "visible", is_combined, + NULL); + sysprof_color_cycle_next (cycle, &rgba); + sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba); + sysprof_visualizer_group_insert (group, + SYSPROF_VISUALIZER (row), + is_combined ? 0 : -1, + !is_combined); + } + } + + if (counters->len > 0) + sysprof_display_add_group (present->display, group); + else + gtk_widget_destroy (GTK_WIDGET (group)); + } + + return counters != NULL; +} + +static void +sysprof_battery_aid_class_init (SysprofBatteryAidClass *klass) +{ + SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass); + + aid_class->prepare = sysprof_battery_aid_prepare; + aid_class->present_async = sysprof_battery_aid_present_async; + aid_class->present_finish = sysprof_battery_aid_present_finish; +} + +static void +sysprof_battery_aid_init (SysprofBatteryAid *self) +{ + sysprof_aid_set_display_name (SYSPROF_AID (self), _("Battery")); + sysprof_aid_set_icon_name (SYSPROF_AID (self), "battery-low-charging-symbolic"); +} diff --git a/src/libsysprof-ui/sysprof-depth-visualizer-row.h b/src/libsysprof-ui/sysprof-battery-aid.h similarity index 73% rename from src/libsysprof-ui/sysprof-depth-visualizer-row.h rename to src/libsysprof-ui/sysprof-battery-aid.h index f413f8d2..563793f1 100644 --- a/src/libsysprof-ui/sysprof-depth-visualizer-row.h +++ b/src/libsysprof-ui/sysprof-battery-aid.h @@ -1,4 +1,4 @@ -/* sysprof-depth-visualizer-row.h +/* sysprof-battery-aid.h * * Copyright 2019 Christian Hergert * @@ -20,12 +20,14 @@ #pragma once -#include "sysprof-visualizer-row.h" +#include "sysprof-aid.h" G_BEGIN_DECLS -#define SYSPROF_TYPE_DEPTH_VISUALIZER_ROW (sysprof_depth_visualizer_row_get_type()) +#define SYSPROF_TYPE_BATTERY_AID (sysprof_battery_aid_get_type()) -G_DECLARE_FINAL_TYPE (SysprofDepthVisualizerRow, sysprof_depth_visualizer_row, SYSPROF, DEPTH_VISUALIZER_ROW, SysprofVisualizerRow) +G_DECLARE_FINAL_TYPE (SysprofBatteryAid, sysprof_battery_aid, SYSPROF, BATTERY_AID, SysprofAid) + +SysprofAid *sysprof_battery_aid_new (void); G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-callgraph-aid.c b/src/libsysprof-ui/sysprof-callgraph-aid.c index 9e3c45a2..5b0ebb07 100644 --- a/src/libsysprof-ui/sysprof-callgraph-aid.c +++ b/src/libsysprof-ui/sysprof-callgraph-aid.c @@ -25,14 +25,46 @@ #include #include "sysprof-callgraph-aid.h" +#include "sysprof-callgraph-page.h" +#include "sysprof-depth-visualizer.h" struct _SysprofCallgraphAid { SysprofAid parent_instance; }; +typedef struct +{ + SysprofCaptureCursor *cursor; + SysprofDisplay *display; + guint has_samples : 1; +} Present; + G_DEFINE_TYPE (SysprofCallgraphAid, sysprof_callgraph_aid, SYSPROF_TYPE_AID) +static void +present_free (gpointer data) +{ + Present *p = data; + + g_clear_pointer (&p->cursor, sysprof_capture_cursor_unref); + g_clear_object (&p->display); + g_slice_free (Present, p); +} + +static void +on_group_activated_cb (SysprofVisualizerGroup *group, + SysprofPage *page) +{ + SysprofDisplay *display; + + g_assert (SYSPROF_IS_VISUALIZER_GROUP (group)); + g_assert (SYSPROF_IS_PAGE (page)); + + display = SYSPROF_DISPLAY (gtk_widget_get_ancestor (GTK_WIDGET (page), SYSPROF_TYPE_DISPLAY)); + sysprof_display_set_visible_page (display, page); +} + /** * sysprof_callgraph_aid_new: * @@ -82,12 +114,157 @@ sysprof_callgraph_aid_prepare (SysprofAid *self, #endif } +static gboolean +discover_samples_cb (const SysprofCaptureFrame *frame, + gpointer user_data) +{ + Present *p = user_data; + + g_assert (frame != NULL); + g_assert (p != NULL); + + if (frame->type == SYSPROF_CAPTURE_FRAME_SAMPLE) + { + p->has_samples = TRUE; + return FALSE; + } + + return TRUE; +} + +static void +sysprof_callgraph_aid_present_worker (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + Present *p = task_data; + + g_assert (G_IS_TASK (task)); + g_assert (SYSPROF_IS_CALLGRAPH_AID (source_object)); + g_assert (p != NULL); + g_assert (p->cursor != NULL); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + /* If we find a sample frame, then we should enable the callgraph + * and stack visualizers. + */ + sysprof_capture_cursor_foreach (p->cursor, discover_samples_cb, p); + g_task_return_boolean (task, TRUE); +} + +static void +sysprof_callgraph_aid_present_async (SysprofAid *aid, + SysprofCaptureReader *reader, + SysprofDisplay *display, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + static const SysprofCaptureFrameType types[] = { SYSPROF_CAPTURE_FRAME_SAMPLE }; + g_autoptr(SysprofCaptureCondition) condition = NULL; + g_autoptr(SysprofCaptureCursor) cursor = NULL; + g_autoptr(GTask) task = NULL; + Present present; + + g_assert (SYSPROF_IS_CALLGRAPH_AID (aid)); + g_assert (reader != NULL); + g_assert (SYSPROF_IS_DISPLAY (display)); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + condition = sysprof_capture_condition_new_where_type_in (1, types); + cursor = sysprof_capture_cursor_new (reader); + sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition)); + + present.cursor = g_steal_pointer (&cursor); + present.display = g_object_ref (display); + + task = g_task_new (aid, cancellable, callback, user_data); + g_task_set_source_tag (task, sysprof_callgraph_aid_present_async); + g_task_set_task_data (task, + g_slice_dup (Present, &present), + present_free); + g_task_run_in_thread (task, sysprof_callgraph_aid_present_worker); +} + +static gboolean +sysprof_callgraph_aid_present_finish (SysprofAid *aid, + GAsyncResult *result, + GError **error) +{ + Present *p; + + g_assert (SYSPROF_IS_CALLGRAPH_AID (aid)); + g_assert (G_IS_TASK (result)); + + p = g_task_get_task_data (G_TASK (result)); + + if (p->has_samples) + { + SysprofVisualizerGroup *group; + SysprofVisualizer *depth; + SysprofPage *page; + + group = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP, + "can-focus", TRUE, + "has-page", TRUE, + "priority", -500, + "title", _("Stack Traces"), + "visible", TRUE, + NULL); + + depth = sysprof_depth_visualizer_new (SYSPROF_DEPTH_VISUALIZER_COMBINED); + g_object_set (depth, + "title", _("Stack Traces"), + "height-request", 35, + "visible", TRUE, + NULL); + sysprof_visualizer_group_insert (group, depth, 0, FALSE); + + depth = sysprof_depth_visualizer_new (SYSPROF_DEPTH_VISUALIZER_KERNEL_ONLY); + g_object_set (depth, + "title", _("Stack Traces (In Kernel)"), + "height-request", 35, + "visible", FALSE, + NULL); + sysprof_visualizer_group_insert (group, depth, 1, TRUE); + + depth = sysprof_depth_visualizer_new (SYSPROF_DEPTH_VISUALIZER_USER_ONLY); + g_object_set (depth, + "title", _("Stack Traces (In User)"), + "height-request", 35, + "visible", FALSE, + NULL); + sysprof_visualizer_group_insert (group, depth, 2, TRUE); + + sysprof_display_add_group (p->display, group); + + page = g_object_new (SYSPROF_TYPE_CALLGRAPH_PAGE, + "title", _("Callgraph"), + "vexpand", TRUE, + "visible", TRUE, + NULL); + sysprof_display_add_page (p->display, page); + sysprof_display_set_visible_page (p->display, page); + + g_signal_connect_object (group, + "group-activated", + G_CALLBACK (on_group_activated_cb), + page, + 0); + } + + return g_task_propagate_boolean (G_TASK (result), error); +} + static void sysprof_callgraph_aid_class_init (SysprofCallgraphAidClass *klass) { SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass); aid_class->prepare = sysprof_callgraph_aid_prepare; + aid_class->present_async = sysprof_callgraph_aid_present_async; + aid_class->present_finish = sysprof_callgraph_aid_present_finish; } static void diff --git a/src/libsysprof-ui/sysprof-callgraph-aid.h b/src/libsysprof-ui/sysprof-callgraph-aid.h index ecfe053d..3fdde80a 100644 --- a/src/libsysprof-ui/sysprof-callgraph-aid.h +++ b/src/libsysprof-ui/sysprof-callgraph-aid.h @@ -26,10 +26,8 @@ G_BEGIN_DECLS #define SYSPROF_TYPE_CALLGRAPH_AID (sysprof_callgraph_aid_get_type()) -SYSPROF_AVAILABLE_IN_ALL G_DECLARE_FINAL_TYPE (SysprofCallgraphAid, sysprof_callgraph_aid, SYSPROF, CALLGRAPH_AID, SysprofAid) -SYSPROF_AVAILABLE_IN_ALL SysprofAid *sysprof_callgraph_aid_new (void); G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-callgraph-page.c b/src/libsysprof-ui/sysprof-callgraph-page.c new file mode 100644 index 00000000..354cdf1c --- /dev/null +++ b/src/libsysprof-ui/sysprof-callgraph-page.c @@ -0,0 +1,1293 @@ +/* sysprof-callgraph-page.c + * + * Copyright 2016-2019 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +/* Sysprof -- Sampling, systemwide CPU profiler + * Copyright 2004, Red Hat, Inc. + * Copyright 2004, 2005, 2006, Soeren Sandmann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include + +#include "../stackstash.h" + +#include "sysprof-callgraph-page.h" +#include "sysprof-cell-renderer-percent.h" + +typedef struct +{ + SysprofCallgraphProfile *profile; + + GtkTreeView *callers_view; + GtkTreeView *functions_view; + GtkTreeView *descendants_view; + GtkTreeViewColumn *descendants_name_column; + GtkStack *stack; + + GQueue *history; + + guint profile_size; + guint loading; +} SysprofCallgraphPagePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (SysprofCallgraphPage, sysprof_callgraph_page, SYSPROF_TYPE_PAGE) + +enum { + PROP_0, + PROP_PROFILE, + N_PROPS +}; + +enum { + GO_PREVIOUS, + N_SIGNALS +}; + +enum { + COLUMN_NAME, + COLUMN_SELF, + COLUMN_TOTAL, + COLUMN_POINTER, + COLUMN_HITS, +}; + +static void sysprof_callgraph_page_update_descendants (SysprofCallgraphPage *self, + StackNode *node); + +static GParamSpec *properties [N_PROPS]; +static guint signals [N_SIGNALS]; + +static guint +sysprof_callgraph_page_get_profile_size (SysprofCallgraphPage *self) +{ + SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); + StackStash *stash; + StackNode *node; + guint size = 0; + + g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self)); + + if (priv->profile_size != 0) + return priv->profile_size; + + if (priv->profile == NULL) + return 0; + + if (NULL == (stash = sysprof_callgraph_profile_get_stash (priv->profile))) + return 0; + + for (node = stack_stash_get_root (stash); node != NULL; node = node->siblings) + size += node->total; + + priv->profile_size = size; + + return size; +} + +static void +build_functions_store (StackNode *node, + gpointer user_data) +{ + struct { + GtkListStore *store; + gdouble profile_size; + } *state = user_data; + GtkTreeIter iter; + const StackNode *n; + guint size = 0; + guint total = 0; + + g_assert (state != NULL); + g_assert (GTK_IS_LIST_STORE (state->store)); + + for (n = node; n != NULL; n = n->next) + { + size += n->size; + if (n->toplevel) + total += n->total; + } + + gtk_list_store_append (state->store, &iter); + gtk_list_store_set (state->store, &iter, + COLUMN_NAME, U64_TO_POINTER(node->data), + COLUMN_SELF, 100.0 * size / state->profile_size, + COLUMN_TOTAL, 100.0 * total / state->profile_size, + COLUMN_POINTER, node, + -1); + +} + +static void +sysprof_callgraph_page_load (SysprofCallgraphPage *self, + SysprofCallgraphProfile *profile) +{ + SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); + GtkListStore *functions; + StackStash *stash; + StackNode *n; + GtkTreeIter iter; + struct { + GtkListStore *store; + gdouble profile_size; + } state = { 0 }; + + g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self)); + g_assert (SYSPROF_IS_CALLGRAPH_PROFILE (profile)); + + /* + * TODO: This is probably the type of thing we want to do off the main + * thread. We should be able to build the tree models off thread + * and then simply apply them on the main thread. + * + * In the mean time, we should set the state of the widget to + * insensitive and give some indication of loading progress. + */ + + if (!g_set_object (&priv->profile, profile)) + return; + + if (sysprof_callgraph_profile_is_empty (profile)) + return; + + stash = sysprof_callgraph_profile_get_stash (profile); + + for (n = stack_stash_get_root (stash); n; n = n->siblings) + state.profile_size += n->total; + + functions = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_POINTER); + + state.store = functions; + stack_stash_foreach_by_address (stash, build_functions_store, &state); + + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (functions), + COLUMN_TOTAL, + GTK_SORT_DESCENDING); + + gtk_tree_view_set_model (priv->functions_view, GTK_TREE_MODEL (functions)); + gtk_tree_view_set_model (priv->callers_view, NULL); + gtk_tree_view_set_model (priv->descendants_view, NULL); + + if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (functions), &iter)) + { + GtkTreeSelection *selection; + + selection = gtk_tree_view_get_selection (priv->functions_view); + gtk_tree_selection_select_iter (selection, &iter); + } + + gtk_stack_set_visible_child_name (priv->stack, "callgraph"); + + g_clear_object (&functions); +} + +void +_sysprof_callgraph_page_set_failed (SysprofCallgraphPage *self) +{ + SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); + + g_return_if_fail (SYSPROF_IS_CALLGRAPH_PAGE (self)); + + gtk_stack_set_visible_child_name (priv->stack, "empty-state"); +} + +static void +sysprof_callgraph_page_unload (SysprofCallgraphPage *self) +{ + SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); + + g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self)); + g_assert (SYSPROF_IS_CALLGRAPH_PROFILE (priv->profile)); + + g_queue_clear (priv->history); + g_clear_object (&priv->profile); + priv->profile_size = 0; + + gtk_tree_view_set_model (priv->callers_view, NULL); + gtk_tree_view_set_model (priv->functions_view, NULL); + gtk_tree_view_set_model (priv->descendants_view, NULL); + + gtk_stack_set_visible_child_name (priv->stack, "empty-state"); +} + +/** + * sysprof_callgraph_page_get_profile: + * + * Returns: (transfer none): An #SysprofCallgraphProfile. + */ +SysprofCallgraphProfile * +sysprof_callgraph_page_get_profile (SysprofCallgraphPage *self) +{ + SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); + + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_PAGE (self), NULL); + + return priv->profile; +} + +void +sysprof_callgraph_page_set_profile (SysprofCallgraphPage *self, + SysprofCallgraphProfile *profile) +{ + SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); + + g_return_if_fail (SYSPROF_IS_CALLGRAPH_PAGE (self)); + g_return_if_fail (!profile || SYSPROF_IS_CALLGRAPH_PROFILE (profile)); + + if (profile != priv->profile) + { + if (priv->profile) + sysprof_callgraph_page_unload (self); + + if (profile) + sysprof_callgraph_page_load (self, profile); + + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PROFILE]); + } +} + +static void +sysprof_callgraph_page_expand_descendants (SysprofCallgraphPage *self) +{ + SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); + GtkTreeModel *model; + GList *all_paths = NULL; + GtkTreePath *first_path; + GtkTreeIter iter; + gdouble top_value = 0; + gint max_rows = 40; /* FIXME */ + gint n_rows; + + g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self)); + + model = gtk_tree_view_get_model (priv->descendants_view); + first_path = gtk_tree_path_new_first (); + all_paths = g_list_prepend (all_paths, first_path); + n_rows = 1; + + gtk_tree_model_get_iter (model, &iter, first_path); + gtk_tree_model_get (model, &iter, + COLUMN_TOTAL, &top_value, + -1); + + while ((all_paths != NULL) && (n_rows < max_rows)) + { + GtkTreeIter best_iter; + GtkTreePath *best_path = NULL; + GList *list; + gdouble best_value = 0.0; + gint n_children; + gint i; + + for (list = all_paths; list != NULL; list = list->next) + { + GtkTreePath *path = list->data; + + g_assert (path != NULL); + + if (gtk_tree_model_get_iter (model, &iter, path)) + { + gdouble value; + + gtk_tree_model_get (model, &iter, + COLUMN_TOTAL, &value, + -1); + + if (value >= best_value) + { + best_value = value; + best_path = path; + best_iter = iter; + } + } + } + + n_children = gtk_tree_model_iter_n_children (model, &best_iter); + + if ((n_children > 0) && + ((best_value / top_value) > 0.04) && + ((n_children + gtk_tree_path_get_depth (best_path)) / (gdouble)max_rows) < (best_value / top_value)) + { + gtk_tree_view_expand_row (priv->descendants_view, best_path, FALSE); + n_rows += n_children; + + if (gtk_tree_path_get_depth (best_path) < 4) + { + GtkTreePath *path; + + path = gtk_tree_path_copy (best_path); + gtk_tree_path_down (path); + + for (i = 0; i < n_children; i++) + { + all_paths = g_list_prepend (all_paths, path); + + path = gtk_tree_path_copy (path); + gtk_tree_path_next (path); + } + + gtk_tree_path_free (path); + } + } + + all_paths = g_list_remove (all_paths, best_path); + + /* Always expand at least once */ + if ((all_paths == NULL) && (n_rows == 1)) + gtk_tree_view_expand_row (priv->descendants_view, best_path, FALSE); + + gtk_tree_path_free (best_path); + } + + g_list_free_full (all_paths, (GDestroyNotify)gtk_tree_path_free); +} + +typedef struct +{ + StackNode *node; + const gchar *name; + guint self; + guint total; +} Caller; + +static Caller * +caller_new (StackNode *node) +{ + Caller *c; + + c = g_slice_new (Caller); + c->name = U64_TO_POINTER (node->data); + c->self = 0; + c->total = 0; + c->node = node; + + return c; +} + +static void +caller_free (gpointer data) +{ + Caller *c = data; + g_slice_free (Caller, c); +} + +static void +sysprof_callgraph_page_function_selection_changed (SysprofCallgraphPage *self, + GtkTreeSelection *selection) +{ + SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); + GtkTreeModel *model = NULL; + GtkTreeIter iter; + GtkListStore *callers_store; + g_autoptr(GHashTable) callers = NULL; + g_autoptr(GHashTable) processed = NULL; + StackNode *callees = NULL; + StackNode *node; + + g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self)); + g_assert (GTK_IS_TREE_SELECTION (selection)); + + if (!gtk_tree_selection_get_selected (selection, &model, &iter)) + { + gtk_tree_view_set_model (priv->callers_view, NULL); + gtk_tree_view_set_model (priv->descendants_view, NULL); + return; + } + + gtk_tree_model_get (model, &iter, + COLUMN_POINTER, &callees, + -1); + + sysprof_callgraph_page_update_descendants (self, callees); + + callers_store = gtk_list_store_new (4, + G_TYPE_STRING, + G_TYPE_DOUBLE, + G_TYPE_DOUBLE, + G_TYPE_POINTER); + + callers = g_hash_table_new_full (NULL, NULL, NULL, caller_free); + processed = g_hash_table_new (NULL, NULL); + + for (node = callees; node != NULL; node = node->next) + { + Caller *c; + + if (!node->parent) + continue; + + c = g_hash_table_lookup (callers, U64_TO_POINTER (node->parent->data)); + + if (c == NULL) + { + c = caller_new (node->parent); + g_hash_table_insert (callers, (gpointer)c->name, c); + } + } + + for (node = callees; node != NULL; node = node->next) + { + StackNode *top_caller = node->parent; + StackNode *top_callee = node; + StackNode *n; + Caller *c; + + if (!node->parent) + continue; + + /* + * We could have a situation where the function was called in a + * reentrant fashion, so we want to take the top-most match in the + * stack. + */ + for (n = node; n && n->parent; n = n->parent) + { + if (n->data == node->data && n->parent->data == node->parent->data) + { + top_caller = n->parent; + top_callee = n; + } + } + + c = g_hash_table_lookup (callers, U64_TO_POINTER (node->parent->data)); + + g_assert (c != NULL); + + if (!g_hash_table_lookup (processed, top_caller)) + { + c->total += top_callee->total; + g_hash_table_insert (processed, top_caller, top_caller); + } + + c->self += node->size; + } + + { + GHashTableIter hiter; + gpointer key, value; + guint size = 0; + + size = MAX (1, sysprof_callgraph_page_get_profile_size (self)); + + g_hash_table_iter_init (&hiter, callers); + + while (g_hash_table_iter_next (&hiter, &key, &value)) + { + Caller *c = value; + + gtk_list_store_append (callers_store, &iter); + gtk_list_store_set (callers_store, &iter, + COLUMN_NAME, c->name, + COLUMN_SELF, c->self * 100.0 / size, + COLUMN_TOTAL, c->total * 100.0 / size, + COLUMN_POINTER, c->node, + -1); + } + } + + gtk_tree_view_set_model (priv->callers_view, GTK_TREE_MODEL (callers_store)); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (callers_store), + COLUMN_TOTAL, + GTK_SORT_DESCENDING); + + g_clear_object (&callers_store); +} + +static void +sysprof_callgraph_page_set_node (SysprofCallgraphPage *self, + StackNode *node) +{ + SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); + GtkTreeModel *model; + GtkTreeIter iter; + + g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self)); + g_assert (node != NULL); + + if (priv->profile == NULL) + return; + + model = gtk_tree_view_get_model (priv->functions_view); + + if (gtk_tree_model_get_iter_first (model, &iter)) + { + do + { + StackNode *item = NULL; + + gtk_tree_model_get (model, &iter, + COLUMN_POINTER, &item, + -1); + + if (item != NULL && item->data == node->data) + { + GtkTreeSelection *selection; + + selection = gtk_tree_view_get_selection (priv->functions_view); + gtk_tree_selection_select_iter (selection, &iter); + + break; + } + } + while (gtk_tree_model_iter_next (model, &iter)); + } +} + +static void +sysprof_callgraph_page_descendant_activated (SysprofCallgraphPage *self, + GtkTreePath *path, + GtkTreeViewColumn *column, + GtkTreeView *tree_view) +{ + GtkTreeModel *model; + StackNode *node = NULL; + GtkTreeIter iter; + + g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self)); + g_assert (GTK_IS_TREE_VIEW (tree_view)); + g_assert (path != NULL); + g_assert (GTK_IS_TREE_VIEW_COLUMN (column)); + + model = gtk_tree_view_get_model (tree_view); + + if (!gtk_tree_model_get_iter (model, &iter, path)) + return; + + gtk_tree_model_get (model, &iter, + COLUMN_POINTER, &node, + -1); + + if (node != NULL) + sysprof_callgraph_page_set_node (self, node); +} + +static void +sysprof_callgraph_page_caller_activated (SysprofCallgraphPage *self, + GtkTreePath *path, + GtkTreeViewColumn *column, + GtkTreeView *tree_view) +{ + GtkTreeModel *model; + StackNode *node = NULL; + GtkTreeIter iter; + + g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self)); + g_assert (GTK_IS_TREE_VIEW (tree_view)); + g_assert (path != NULL); + g_assert (GTK_IS_TREE_VIEW_COLUMN (column)); + + model = gtk_tree_view_get_model (tree_view); + + if (!gtk_tree_model_get_iter (model, &iter, path)) + return; + + gtk_tree_model_get (model, &iter, + COLUMN_POINTER, &node, + -1); + + if (node != NULL) + sysprof_callgraph_page_set_node (self, node); +} + +static void +sysprof_callgraph_page_tag_data_func (GtkTreeViewColumn *column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + SysprofCallgraphPage *self = data; + SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); + StackNode *node = NULL; + const gchar *str = NULL; + + if (priv->profile == NULL) + return; + + gtk_tree_model_get (model, iter, COLUMN_POINTER, &node, -1); + + if (node && node->data) + { + GQuark tag; + + tag = sysprof_callgraph_profile_get_tag (priv->profile, GSIZE_TO_POINTER (node->data)); + if (tag != 0) + str = g_quark_to_string (tag); + } + + g_object_set (cell, "text", str, NULL); +} + +static void +sysprof_callgraph_page_real_go_previous (SysprofCallgraphPage *self) +{ + SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); + StackNode *node; + + g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self)); + + node = g_queue_pop_head (priv->history); + + if (NULL != (node = g_queue_peek_head (priv->history))) + sysprof_callgraph_page_set_node (self, node); +} + +static void +descendants_view_move_cursor_cb (GtkTreeView *descendants_view, + GtkMovementStep step, + int direction, + gpointer user_data) +{ + if (step == GTK_MOVEMENT_VISUAL_POSITIONS) + { + GtkTreePath *path; + + gtk_tree_view_get_cursor (descendants_view, &path, NULL); + + if (direction == 1) + { + gtk_tree_view_expand_row (descendants_view, path, FALSE); + g_signal_stop_emission_by_name (descendants_view, "move-cursor"); + } + else if (direction == -1) + { + gtk_tree_view_collapse_row (descendants_view, path); + g_signal_stop_emission_by_name (descendants_view, "move-cursor"); + } + + gtk_tree_path_free (path); + } +} + +static void +copy_tree_view_selection_cb (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + g_autofree gchar *name = NULL; + gchar sstr[16]; + gchar tstr[16]; + GString *str = data; + gdouble self; + gdouble total; + gint depth; + + g_assert (GTK_IS_TREE_MODEL (model)); + g_assert (path != NULL); + g_assert (iter != NULL); + g_assert (str != NULL); + + depth = gtk_tree_path_get_depth (path); + gtk_tree_model_get (model, iter, + COLUMN_NAME, &name, + COLUMN_SELF, &self, + COLUMN_TOTAL, &total, + -1); + + g_snprintf (sstr, sizeof sstr, "%.2lf%%", self); + g_snprintf (tstr, sizeof tstr, "%.2lf%%", total); + + g_string_append_printf (str, "[%8s] [%8s] ", sstr, tstr); + + for (gint i = 1; i < depth; i++) + g_string_append (str, " "); + g_string_append (str, name); + g_string_append_c (str, '\n'); +} + +static void +copy_tree_view_selection (GtkTreeView *tree_view) +{ + g_autoptr(GString) str = NULL; + GtkClipboard *clipboard; + + g_assert (GTK_IS_TREE_VIEW (tree_view)); + + str = g_string_new (" SELF TOTAL FUNCTION\n"); + gtk_tree_selection_selected_foreach (gtk_tree_view_get_selection (tree_view), + copy_tree_view_selection_cb, + str); + + clipboard = gtk_widget_get_clipboard (GTK_WIDGET (tree_view), GDK_SELECTION_CLIPBOARD); + gtk_clipboard_set_text (clipboard, str->str, str->len); +} + +static void +sysprof_callgraph_page_copy_cb (GtkWidget *widget, + SysprofCallgraphPage *self) +{ + SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); + GtkWidget *toplevel; + GtkWidget *focus; + + g_assert (GTK_IS_WIDGET (widget)); + g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self)); + + if (!(toplevel = gtk_widget_get_toplevel (widget)) || + !GTK_IS_WINDOW (toplevel) || + !(focus = gtk_window_get_focus (GTK_WINDOW (toplevel)))) + return; + + if (focus == GTK_WIDGET (priv->descendants_view)) + copy_tree_view_selection (priv->descendants_view); + else if (focus == GTK_WIDGET (priv->callers_view)) + copy_tree_view_selection (priv->callers_view); + else if (focus == GTK_WIDGET (priv->functions_view)) + copy_tree_view_selection (priv->functions_view); +} + +static void +sysprof_callgraph_page_generate_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + SysprofProfile *profile = (SysprofProfile *)object; + SysprofCallgraphPage *self; + g_autoptr(GTask) task = user_data; + g_autoptr(GError) error = NULL; + + g_assert (SYSPROF_IS_PROFILE (profile)); + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (G_IS_TASK (task)); + + self = g_task_get_source_object (task); + + if (!sysprof_profile_generate_finish (profile, result, &error)) + g_task_return_error (task, g_steal_pointer (&error)); + else + sysprof_callgraph_page_set_profile (self, SYSPROF_CALLGRAPH_PROFILE (profile)); +} + +static void +sysprof_callgraph_page_load_async (SysprofPage *page, + SysprofCaptureReader *reader, + SysprofSelection *selection, + SysprofCaptureCondition *filter, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + SysprofCallgraphPage *self = (SysprofCallgraphPage *)page; + g_autoptr(SysprofCaptureReader) copy = NULL; + g_autoptr(SysprofProfile) profile = NULL; + g_autoptr(GTask) task = NULL; + + g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self)); + g_assert (reader != NULL); + g_assert (SYSPROF_IS_SELECTION (selection)); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, sysprof_callgraph_page_load_async); + + copy = sysprof_capture_reader_copy (reader); + + profile = sysprof_callgraph_profile_new_with_selection (selection); + sysprof_profile_set_reader (profile, reader); + sysprof_profile_generate (profile, + cancellable, + sysprof_callgraph_page_generate_cb, + g_steal_pointer (&task)); +} + +static gboolean +sysprof_callgraph_page_load_finish (SysprofPage *page, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_PAGE (page), FALSE); + g_return_val_if_fail (G_IS_TASK (result), FALSE); + + return g_task_propagate_boolean (G_TASK (result), error); +} + +static void +sysprof_callgraph_page_finalize (GObject *object) +{ + SysprofCallgraphPage *self = (SysprofCallgraphPage *)object; + SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); + + g_clear_pointer (&priv->history, g_queue_free); + g_clear_object (&priv->profile); + + G_OBJECT_CLASS (sysprof_callgraph_page_parent_class)->finalize (object); +} + +static void +sysprof_callgraph_page_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofCallgraphPage *self = SYSPROF_CALLGRAPH_PAGE (object); + SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); + + switch (prop_id) + { + case PROP_PROFILE: + g_value_set_object (value, priv->profile); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_callgraph_page_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofCallgraphPage *self = SYSPROF_CALLGRAPH_PAGE (object); + + switch (prop_id) + { + case PROP_PROFILE: + sysprof_callgraph_page_set_profile (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_callgraph_page_class_init (SysprofCallgraphPageClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + SysprofPageClass *page_class = SYSPROF_PAGE_CLASS (klass); + GtkBindingSet *bindings; + + object_class->finalize = sysprof_callgraph_page_finalize; + object_class->get_property = sysprof_callgraph_page_get_property; + object_class->set_property = sysprof_callgraph_page_set_property; + + page_class->load_async = sysprof_callgraph_page_load_async; + page_class->load_finish = sysprof_callgraph_page_load_finish; + + klass->go_previous = sysprof_callgraph_page_real_go_previous; + + properties [PROP_PROFILE] = + g_param_spec_object ("profile", + "Profile", + "The callgraph profile to view", + SYSPROF_TYPE_CALLGRAPH_PROFILE, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); + + signals [GO_PREVIOUS] = + g_signal_new ("go-previous", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (SysprofCallgraphPageClass, go_previous), + NULL, NULL, NULL, G_TYPE_NONE, 0); + + gtk_widget_class_set_template_from_resource (widget_class, + "/org/gnome/sysprof/ui/sysprof-callgraph-page.ui"); + + gtk_widget_class_bind_template_child_private (widget_class, SysprofCallgraphPage, callers_view); + gtk_widget_class_bind_template_child_private (widget_class, SysprofCallgraphPage, functions_view); + gtk_widget_class_bind_template_child_private (widget_class, SysprofCallgraphPage, descendants_view); + gtk_widget_class_bind_template_child_private (widget_class, SysprofCallgraphPage, descendants_name_column); + gtk_widget_class_bind_template_child_private (widget_class, SysprofCallgraphPage, stack); + + bindings = gtk_binding_set_by_class (klass); + gtk_binding_entry_add_signal (bindings, GDK_KEY_Left, GDK_MOD1_MASK, "go-previous", 0); + + g_type_ensure (SYSPROF_TYPE_CELL_RENDERER_PERCENT); +} + +static void +sysprof_callgraph_page_init (SysprofCallgraphPage *self) +{ + SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); + DzlShortcutController *controller; + GtkTreeSelection *selection; + GtkCellRenderer *cell; + + priv->history = g_queue_new (); + + gtk_widget_init_template (GTK_WIDGET (self)); + + gtk_stack_set_visible_child_name (priv->stack, "empty-state"); + + selection = gtk_tree_view_get_selection (priv->functions_view); + + g_signal_connect_object (selection, + "changed", + G_CALLBACK (sysprof_callgraph_page_function_selection_changed), + self, + G_CONNECT_SWAPPED); + + g_signal_connect_object (priv->descendants_view, + "row-activated", + G_CALLBACK (sysprof_callgraph_page_descendant_activated), + self, + G_CONNECT_SWAPPED); + + g_signal_connect_object (priv->callers_view, + "row-activated", + G_CALLBACK (sysprof_callgraph_page_caller_activated), + self, + G_CONNECT_SWAPPED); + + g_signal_connect (priv->descendants_view, + "move-cursor", + G_CALLBACK (descendants_view_move_cursor_cb), + NULL); + + cell = g_object_new (GTK_TYPE_CELL_RENDERER_TEXT, + "ellipsize", PANGO_ELLIPSIZE_MIDDLE, + "xalign", 0.0f, + NULL); + gtk_tree_view_column_pack_start (priv->descendants_name_column, cell, TRUE); + gtk_tree_view_column_add_attribute (priv->descendants_name_column, cell, "text", 0); + + cell = g_object_new (GTK_TYPE_CELL_RENDERER_TEXT, + "foreground", "#666666", + "scale", PANGO_SCALE_SMALL, + "xalign", 1.0f, + NULL); + gtk_tree_view_column_pack_start (priv->descendants_name_column, cell, FALSE); + gtk_tree_view_column_set_cell_data_func (priv->descendants_name_column, cell, + sysprof_callgraph_page_tag_data_func, + self, NULL); + + gtk_tree_selection_set_mode (gtk_tree_view_get_selection (priv->descendants_view), + GTK_SELECTION_MULTIPLE); + + controller = dzl_shortcut_controller_find (GTK_WIDGET (self)); + + dzl_shortcut_controller_add_command_callback (controller, + "org.gnome.sysprof3.capture.copy", + "c", + DZL_SHORTCUT_PHASE_BUBBLE, + (GtkCallback) sysprof_callgraph_page_copy_cb, + self, + NULL); +} + +typedef struct _Descendant Descendant; + +struct _Descendant +{ + const gchar *name; + guint self; + guint cumulative; + Descendant *parent; + Descendant *siblings; + Descendant *children; +}; + +static void +build_tree_cb (StackLink *trace, + gint size, + gpointer user_data) +{ + Descendant **tree = user_data; + Descendant *parent = NULL; + StackLink *link; + + g_assert (trace != NULL); + g_assert (tree != NULL); + + /* Get last item */ + link = trace; + while (link->next) + link = link->next; + + for (; link != NULL; link = link->prev) + { + const gchar *address = U64_TO_POINTER (link->data); + Descendant *prev = NULL; + Descendant *match = NULL; + + for (match = *tree; match != NULL; match = match->siblings) + { + if (match->name == address) + { + if (prev != NULL) + { + /* Move to front */ + prev->siblings = match->siblings; + match->siblings = *tree; + *tree = match; + } + break; + } + } + + if (match == NULL) + { + /* Have we seen this object further up the tree? */ + for (match = parent; match != NULL; match = match->parent) + { + if (match->name == address) + break; + } + } + + if (match == NULL) + { + match = g_slice_new (Descendant); + match->name = address; + match->cumulative = 0; + match->self = 0; + match->children = NULL; + match->parent = parent; + match->siblings = *tree; + *tree = match; + } + + tree = &match->children; + parent = match; + } + + parent->self += size; + + for (; parent != NULL; parent = parent->parent) + parent->cumulative += size; +} + +static Descendant * +build_tree (StackNode *node) +{ + Descendant *tree = NULL; + + for (; node != NULL; node = node->next) + { + if (node->toplevel) + stack_node_foreach_trace (node, build_tree_cb, &tree); + } + + return tree; +} + +static void +append_to_tree_and_free (SysprofCallgraphPage *self, + StackStash *stash, + GtkTreeStore *store, + Descendant *item, + GtkTreeIter *parent) +{ + StackNode *node = NULL; + GtkTreeIter iter; + guint profile_size; + + g_assert (GTK_IS_TREE_STORE (store)); + g_assert (item != NULL); + + profile_size = MAX (1, sysprof_callgraph_page_get_profile_size (self)); + + gtk_tree_store_append (store, &iter, parent); + + node = stack_stash_find_node (stash, (gpointer)item->name); + + gtk_tree_store_set (store, &iter, + COLUMN_NAME, item->name, + COLUMN_SELF, item->self * 100.0 / (gdouble)profile_size, + COLUMN_TOTAL, item->cumulative * 100.0 / (gdouble)profile_size, + COLUMN_POINTER, node, + COLUMN_HITS, (guint)item->cumulative, + -1); + + if (item->siblings != NULL) + append_to_tree_and_free (self, stash, store, item->siblings, parent); + + if (item->children != NULL) + append_to_tree_and_free (self, stash, store, item->children, &iter); + + g_slice_free (Descendant, item); +} + +static void +sysprof_callgraph_page_update_descendants (SysprofCallgraphPage *self, + StackNode *node) +{ + SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); + GtkTreeStore *store; + + g_assert (SYSPROF_IS_CALLGRAPH_PAGE (self)); + + if (g_queue_peek_head (priv->history) != node) + g_queue_push_head (priv->history, node); + + store = gtk_tree_store_new (5, + G_TYPE_STRING, + G_TYPE_DOUBLE, + G_TYPE_DOUBLE, + G_TYPE_POINTER, + G_TYPE_UINT); + + if (priv->profile != NULL) + { + StackStash *stash; + + stash = sysprof_callgraph_profile_get_stash (priv->profile); + if (stash != NULL) + { + Descendant *tree; + + tree = build_tree (node); + if (tree != NULL) + append_to_tree_and_free (self, stash, store, tree, NULL); + } + } + + gtk_tree_view_set_model (priv->descendants_view, GTK_TREE_MODEL (store)); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), + COLUMN_TOTAL, GTK_SORT_DESCENDING); + sysprof_callgraph_page_expand_descendants (self); + + g_clear_object (&store); +} + +/** + * sysprof_callgraph_page_screenshot: + * @self: A #SysprofCallgraphPage. + * + * This function will generate a text representation of the descendants tree. + * This is useful if you want to include various profiling information in a + * commit message or email. + * + * The text generated will match the current row expansion in the tree view. + * + * Returns: (nullable) (transfer full): A newly allocated string that should be freed + * with g_free(). + */ +gchar * +sysprof_callgraph_page_screenshot (SysprofCallgraphPage *self) +{ + SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); + GtkTreeView *tree_view; + GtkTreeModel *model; + GtkTreePath *tree_path; + GString *str; + GtkTreeIter iter; + + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_PAGE (self), NULL); + + tree_view = priv->descendants_view; + + if (NULL == (model = gtk_tree_view_get_model (tree_view))) + return NULL; + + /* + * To avoid having to precalculate the deepest visible row, we + * put the timing information at the beginning of the line. + */ + + str = g_string_new (" SELF CUMULATIVE FUNCTION\n"); + tree_path = gtk_tree_path_new_first (); + + for (;;) + { + if (gtk_tree_model_get_iter (model, &iter, tree_path)) + { + guint depth = gtk_tree_path_get_depth (tree_path); + StackNode *node; + gdouble in_self; + gdouble total; + guint i; + + gtk_tree_model_get (model, &iter, + COLUMN_SELF, &in_self, + COLUMN_TOTAL, &total, + COLUMN_POINTER, &node, + -1); + + g_string_append_printf (str, "[% 7.2lf%%] [% 7.2lf%%] ", in_self, total); + + for (i = 0; i < depth; i++) + g_string_append (str, " "); + g_string_append (str, GSIZE_TO_POINTER (node->data)); + g_string_append_c (str, '\n'); + + if (gtk_tree_view_row_expanded (tree_view, tree_path)) + gtk_tree_path_down (tree_path); + else + gtk_tree_path_next (tree_path); + + continue; + } + + if (!gtk_tree_path_up (tree_path) || !gtk_tree_path_get_depth (tree_path)) + break; + + gtk_tree_path_next (tree_path); + } + + gtk_tree_path_free (tree_path); + + return g_string_free (str, FALSE); +} + +guint +sysprof_callgraph_page_get_n_functions (SysprofCallgraphPage *self) +{ + SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); + GtkTreeModel *model; + guint ret = 0; + + g_return_val_if_fail (SYSPROF_IS_CALLGRAPH_PAGE (self), 0); + + if (NULL != (model = gtk_tree_view_get_model (priv->functions_view))) + ret = gtk_tree_model_iter_n_children (model, NULL); + + return ret; +} + +void +_sysprof_callgraph_page_set_loading (SysprofCallgraphPage *self, + gboolean loading) +{ + SysprofCallgraphPagePrivate *priv = sysprof_callgraph_page_get_instance_private (self); + + g_return_if_fail (SYSPROF_IS_CALLGRAPH_PAGE (self)); + + if (loading) + priv->loading++; + else + priv->loading--; + + if (priv->loading) + gtk_stack_set_visible_child_name (priv->stack, "loading"); + else + gtk_stack_set_visible_child_name (priv->stack, "callgraph"); +} diff --git a/src/libsysprof-ui/sysprof-callgraph-page.h b/src/libsysprof-ui/sysprof-callgraph-page.h new file mode 100644 index 00000000..7624c580 --- /dev/null +++ b/src/libsysprof-ui/sysprof-callgraph-page.h @@ -0,0 +1,51 @@ +/* sysprof-callgraph-page.h + * + * Copyright 2016-2019 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include +#include + +#include "sysprof-page.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_CALLGRAPH_PAGE (sysprof_callgraph_page_get_type()) + +G_DECLARE_DERIVABLE_TYPE (SysprofCallgraphPage, sysprof_callgraph_page, SYSPROF, CALLGRAPH_PAGE, SysprofPage) + +struct _SysprofCallgraphPageClass +{ + SysprofPageClass parent_class; + + void (*go_previous) (SysprofCallgraphPage *self); + + /*< private >*/ + gpointer _reserved[16]; +}; + +GtkWidget *sysprof_callgraph_page_new (void); +SysprofCallgraphProfile *sysprof_callgraph_page_get_profile (SysprofCallgraphPage *self); +void sysprof_callgraph_page_set_profile (SysprofCallgraphPage *self, + SysprofCallgraphProfile *profile); +gchar *sysprof_callgraph_page_screenshot (SysprofCallgraphPage *self); +guint sysprof_callgraph_page_get_n_functions (SysprofCallgraphPage *self); + +G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-callgraph-page.ui b/src/libsysprof-ui/sysprof-callgraph-page.ui new file mode 100644 index 00000000..2642e01b --- /dev/null +++ b/src/libsysprof-ui/sysprof-callgraph-page.ui @@ -0,0 +1,235 @@ + + + diff --git a/src/libsysprof-ui/sysprof-callgraph-view.h b/src/libsysprof-ui/sysprof-callgraph-view.h index 6cd68c5f..7b0c2ec1 100644 --- a/src/libsysprof-ui/sysprof-callgraph-view.h +++ b/src/libsysprof-ui/sysprof-callgraph-view.h @@ -20,10 +20,6 @@ #pragma once -#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION) -# error "Only can be included directly." -#endif - #include #include @@ -31,7 +27,6 @@ G_BEGIN_DECLS #define SYSPROF_TYPE_CALLGRAPH_VIEW (sysprof_callgraph_view_get_type()) -SYSPROF_AVAILABLE_IN_ALL G_DECLARE_DERIVABLE_TYPE (SysprofCallgraphView, sysprof_callgraph_view, SYSPROF, CALLGRAPH_VIEW, GtkBin) struct _SysprofCallgraphViewClass @@ -39,20 +34,13 @@ struct _SysprofCallgraphViewClass GtkBinClass parent_class; void (*go_previous) (SysprofCallgraphView *self); - - gpointer padding[8]; }; -SYSPROF_AVAILABLE_IN_ALL GtkWidget *sysprof_callgraph_view_new (void); -SYSPROF_AVAILABLE_IN_ALL SysprofCallgraphProfile *sysprof_callgraph_view_get_profile (SysprofCallgraphView *self); -SYSPROF_AVAILABLE_IN_ALL void sysprof_callgraph_view_set_profile (SysprofCallgraphView *self, SysprofCallgraphProfile *profile); -SYSPROF_AVAILABLE_IN_ALL gchar *sysprof_callgraph_view_screenshot (SysprofCallgraphView *self); -SYSPROF_AVAILABLE_IN_ALL guint sysprof_callgraph_view_get_n_functions (SysprofCallgraphView *self); G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-capture-view.c b/src/libsysprof-ui/sysprof-capture-view.c deleted file mode 100644 index c43713ec..00000000 --- a/src/libsysprof-ui/sysprof-capture-view.c +++ /dev/null @@ -1,1092 +0,0 @@ -/* sysprof-capture-view.c - * - * Copyright 2019 Christian Hergert - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -#define G_LOG_DOMAIN "sysprof-capture-view" - -#include "config.h" - -#include -#include - -#include "sysprof-callgraph-view.h" -#include "sysprof-capture-view.h" -#include "sysprof-details-view.h" -#include "sysprof-logs-view.h" -#include "sysprof-marks-view.h" -#include "sysprof-ui-private.h" -#include "sysprof-visualizer-view.h" - -#define NSEC_PER_SEC (G_USEC_PER_SEC * 1000L) - -typedef struct -{ - gint64 begin_time; - gint64 end_time; - guint has_samples : 1; - guint has_counters : 1; - guint has_forks : 1; - guint has_logs : 1; - guint has_marks : 1; - guint can_replay : 1; -} SysprofCaptureFeatures; - -typedef struct -{ - SysprofCaptureReader *reader; - GCancellable *cancellable; - GHashTable *mark_stats; - - SysprofCaptureFeatures features; - - /* Template Objects */ - GtkAdjustment *time_adj; - GtkStack *stack; - SysprofCallgraphView *callgraph_view; - SysprofDetailsView *details_view; - SysprofLogsView *logs_view; - SysprofMarksView *counters_view; - SysprofMarksView *marks_view; - SysprofVisualizerView *visualizer_view; - SysprofZoomManager *zoom_manager; - GtkStackSwitcher *stack_switcher; - - guint busy; - - guint can_replay : 1; - guint needs_fit : 1; -} SysprofCaptureViewPrivate; - -typedef struct -{ - SysprofCaptureReader *reader; - SysprofSelection *selection; - gint n_active; - guint has_error : 1; -} LoadAsync; - -G_DEFINE_TYPE_WITH_PRIVATE (SysprofCaptureView, sysprof_capture_view, GTK_TYPE_BIN) - -enum { - PROP_0, - PROP_BUSY, - N_PROPS -}; - -static GParamSpec *properties [N_PROPS]; - -static void -load_async_free (gpointer data) -{ - LoadAsync *state = data; - - if (state != NULL) - { - g_clear_pointer (&state->reader, sysprof_capture_reader_unref); - g_clear_object (&state->selection); - g_slice_free (LoadAsync, state); - } -} - -static gboolean -do_best_fit_in_idle (gpointer user_data) -{ - SysprofCaptureView *self = user_data; - - if (gtk_widget_get_visible (GTK_WIDGET (self))) - sysprof_capture_view_fit_to_width (self); - - return G_SOURCE_REMOVE; -} - -SysprofMarkStat * -_sysprof_mark_stat_new (const gchar *name) -{ - SysprofMarkStat *ret; - - ret = g_slice_new0 (SysprofMarkStat); - ret->name = g_strdup (name); - - return ret; -} - -void -_sysprof_mark_stat_free (SysprofMarkStat *self) -{ - g_clear_pointer (&self->name, g_free); - g_slice_free (SysprofMarkStat, self); -} - -/** - * sysprof_capture_view_new: - * - * Create a new #SysprofCaptureView. - * - * Returns: (transfer full): a newly created #SysprofCaptureView - * - * Since: 3.34 - */ -GtkWidget * -sysprof_capture_view_new (void) -{ - return g_object_new (SYSPROF_TYPE_CAPTURE_VIEW, NULL); -} - -static void -add_marks_to_details (SysprofCaptureView *self) -{ - SysprofCaptureViewPrivate *priv = sysprof_capture_view_get_instance_private (self); - GHashTableIter iter; - gpointer k, v; - - g_assert (SYSPROF_IS_CAPTURE_VIEW (self)); - - if (priv->mark_stats == NULL) - return; - - if (g_hash_table_size (priv->mark_stats) == 0) - return; - - g_hash_table_iter_init (&iter, priv->mark_stats); - while (g_hash_table_iter_next (&iter, &k, &v)) - { - const gchar *name = k; - const SysprofMarkStat *st = v; - - sysprof_details_view_add_mark (priv->details_view, - name, - st->min, - st->max, - st->avg, - st->count); - } -} - -static void -sysprof_capture_view_task_completed (SysprofCaptureView *self, - GParamSpec *pspec, - GTask *task) -{ - SysprofCaptureViewPrivate *priv = sysprof_capture_view_get_instance_private (self); - - g_assert (SYSPROF_IS_CAPTURE_VIEW (self)); - g_assert (G_IS_TASK (task)); - - g_signal_handlers_disconnect_by_func (task, - G_CALLBACK (sysprof_capture_view_task_completed), - self); - - priv->busy--; - - if (priv->busy == 0) - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUSY]); -} - -static void -sysprof_capture_view_monitor_task (SysprofCaptureView *self, - GTask *task) -{ - SysprofCaptureViewPrivate *priv = sysprof_capture_view_get_instance_private (self); - - g_assert (SYSPROF_IS_CAPTURE_VIEW (self)); - g_assert (G_IS_TASK (task)); - - priv->busy++; - - if (priv->busy == 1) - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUSY]); - - g_signal_connect_object (task, - "notify::completed", - G_CALLBACK (sysprof_capture_view_task_completed), - self, - G_CONNECT_SWAPPED); -} - -static void -sysprof_capture_view_generate_callgraph_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofCallgraphProfile *callgraph = (SysprofCallgraphProfile *)object; - g_autoptr(GError) error = NULL; - g_autoptr(GTask) task = user_data; - SysprofCaptureView *self; - SysprofCaptureViewPrivate *priv; - - g_assert (SYSPROF_IS_CALLGRAPH_PROFILE (callgraph)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (G_IS_TASK (task)); - - self = g_task_get_source_object (task); - priv = sysprof_capture_view_get_instance_private (self); - - if (!sysprof_profile_generate_finish (SYSPROF_PROFILE (callgraph), result, &error)) - { - _sysprof_callgraph_view_set_failed (priv->callgraph_view); - g_task_return_error (task, g_steal_pointer (&error)); - return; - } - - sysprof_callgraph_view_set_profile (priv->callgraph_view, callgraph); - - g_task_return_boolean (task, TRUE); -} - -static void -sysprof_capture_view_generate_callgraph_async (SysprofCaptureView *self, - SysprofCaptureReader *reader, - SysprofSelection *selection, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - SysprofCaptureViewPrivate *priv = sysprof_capture_view_get_instance_private (self); - g_autoptr(SysprofCaptureReader) copy = NULL; - g_autoptr(SysprofProfile) callgraph = NULL; - g_autoptr(GTask) task = NULL; - - g_return_if_fail (SYSPROF_IS_CAPTURE_VIEW (self)); - g_return_if_fail (reader != NULL); - g_return_if_fail (!selection || SYSPROF_IS_SELECTION (selection)); - g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); - - task = g_task_new (self, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_capture_view_generate_callgraph_async); - sysprof_capture_view_monitor_task (self, task); - - _sysprof_callgraph_view_set_loading (priv->callgraph_view, TRUE); - - copy = sysprof_capture_reader_copy (reader); - callgraph = sysprof_callgraph_profile_new_with_selection (selection); - sysprof_profile_set_reader (callgraph, copy); - sysprof_profile_generate (callgraph, - cancellable, - sysprof_capture_view_generate_callgraph_cb, - g_steal_pointer (&task)); -} - -static gboolean -sysprof_capture_view_generate_callgraph_finish (SysprofCaptureView *self, - GAsyncResult *result, - GError **error) -{ - SysprofCaptureViewPrivate *priv = sysprof_capture_view_get_instance_private (self); - - g_assert (SYSPROF_IS_CAPTURE_VIEW (self)); - g_assert (G_IS_TASK (result)); - - _sysprof_callgraph_view_set_loading (priv->callgraph_view, FALSE); - - return g_task_propagate_boolean (G_TASK (result), error); -} - -static void -sysprof_capture_view_scan_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - SysprofCaptureView *self = source_object; - SysprofCaptureViewPrivate *priv = sysprof_capture_view_get_instance_private (self); - SysprofCaptureReader *reader = task_data; - g_autoptr(GHashTable) mark_stats = NULL; - SysprofCaptureFeatures features = {0}; - SysprofCaptureFrame frame; - SysprofCaptureStat st_buf = {{0}}; - - g_assert (SYSPROF_IS_CAPTURE_VIEW (self)); - g_assert (G_IS_TASK (task)); - g_assert (reader != NULL); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - mark_stats = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, - (GDestroyNotify)_sysprof_mark_stat_free); - - features.begin_time = sysprof_capture_reader_get_start_time (reader); - features.end_time = sysprof_capture_reader_get_end_time (reader); - - while (sysprof_capture_reader_peek_frame (reader, &frame)) - { - gint64 begin_time = frame.time; - gint64 end_time = G_MININT64; - - g_assert (frame.type < G_N_ELEMENTS (st_buf.frame_count)); - - st_buf.frame_count[frame.type]++; - - if (frame.type == SYSPROF_CAPTURE_FRAME_METADATA) - { - const SysprofCaptureMetadata *meta; - - if ((meta = sysprof_capture_reader_read_metadata (reader))) - { - if (g_strcmp0 (meta->id, "local-profiler") == 0) - features.can_replay = TRUE; - } - } - else if (frame.type == SYSPROF_CAPTURE_FRAME_FORK) - { - features.has_forks = TRUE; - sysprof_capture_reader_read_fork (reader); - } - else if (frame.type == SYSPROF_CAPTURE_FRAME_MARK) - { - const SysprofCaptureMark *mark; - - if ((mark = sysprof_capture_reader_read_mark (reader))) - { - SysprofMarkStat *mstat; - gchar name[128]; - - end_time = frame.time + mark->duration; - - g_snprintf (name, sizeof name, "%s:%s", mark->group, mark->name); - - if (!(mstat = g_hash_table_lookup (mark_stats, name))) - { - mstat = _sysprof_mark_stat_new (name); - g_hash_table_insert (mark_stats, mstat->name, mstat); - } - - if (mark->duration > 0) - { - if (mstat->min == 0 || mark->duration < mstat->min) - mstat->min = mark->duration; - } - - if (mark->duration > mstat->max) - mstat->max = mark->duration; - - if (mark->duration > 0) - { - mstat->avg += mark->duration; - mstat->avg_count++; - } - - mstat->count++; - } - - features.has_marks = TRUE; - } - else if (frame.type == SYSPROF_CAPTURE_FRAME_CTRDEF) - { - features.has_counters = TRUE; - sysprof_capture_reader_skip (reader); - } - else if (frame.type == SYSPROF_CAPTURE_FRAME_LOG) - { - features.has_logs = TRUE; - sysprof_capture_reader_skip (reader); - } - else if (frame.type == SYSPROF_CAPTURE_FRAME_SAMPLE) - { - features.has_samples = TRUE; - sysprof_capture_reader_skip (reader); - } - else - { - sysprof_capture_reader_skip (reader); - } - - if (begin_time < features.begin_time) - features.begin_time = begin_time; - - if (end_time > features.end_time) - features.end_time = end_time; - } - - { - GHashTableIter iter; - gpointer k,v; - - g_hash_table_iter_init (&iter, mark_stats); - while (g_hash_table_iter_next (&iter, &k, &v)) - { - SysprofMarkStat *mstat = v; - - if (mstat->avg_count > 0 && mstat->avg > 0) - mstat->avg /= mstat->avg_count; - -#if 0 - g_print ("%s: count=%ld avg=%ld min=%ld max=%ld\n", - (gchar*)k, - ((SysprofMarkStat *)v)->count, - ((SysprofMarkStat *)v)->avg, - ((SysprofMarkStat *)v)->min, - ((SysprofMarkStat *)v)->max); -#endif - } - } - - sysprof_capture_reader_set_stat (reader, &st_buf); - - g_object_set_data_full (G_OBJECT (task), - "MARK_STAT", - g_steal_pointer (&mark_stats), - (GDestroyNotify) g_hash_table_unref); - - if (!g_task_return_error_if_cancelled (task)) - { - priv->features = features; - sysprof_capture_reader_reset (reader); - g_task_return_boolean (task, TRUE); - } -} - -static void -sysprof_capture_view_scan_async (SysprofCaptureView *self, - SysprofCaptureReader *reader, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - g_autoptr(GTask) task = NULL; - - g_assert (SYSPROF_IS_CAPTURE_VIEW (self)); - g_assert (reader != NULL); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - task = g_task_new (self, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_capture_view_scan_async); - g_task_set_task_data (task, - sysprof_capture_reader_ref (reader), - (GDestroyNotify) sysprof_capture_reader_unref); - sysprof_capture_view_monitor_task (self, task); - g_task_run_in_thread (task, sysprof_capture_view_scan_worker); -} - -static gboolean -sysprof_capture_view_scan_finish (SysprofCaptureView *self, - GAsyncResult *result, - GError **error) -{ - SysprofCaptureViewPrivate *priv = sysprof_capture_view_get_instance_private (self); - GHashTable *stats; - - g_assert (SYSPROF_IS_CAPTURE_VIEW (self)); - g_assert (G_IS_TASK (result)); - - if (!priv->features.has_samples) - gtk_widget_hide (GTK_WIDGET (priv->callgraph_view)); - - if (!priv->features.has_logs) - gtk_widget_hide (GTK_WIDGET (priv->logs_view)); - - if (!priv->features.has_marks && !priv->features.has_forks) - gtk_widget_hide (GTK_WIDGET (priv->marks_view)); - - if (!priv->features.has_counters) - gtk_widget_hide (GTK_WIDGET (priv->counters_view)); - - g_clear_pointer (&priv->mark_stats, g_hash_table_unref); - if ((stats = g_object_get_data (G_OBJECT (result), "MARK_STAT"))) - priv->mark_stats = g_hash_table_ref (stats); - - add_marks_to_details (self); - - priv->needs_fit = TRUE; - - return g_task_propagate_boolean (G_TASK (result), error); -} - -static void -sysprof_capture_view_load_marks_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofMarksView *view = (SysprofMarksView *)object; - g_autoptr(GError) error = NULL; - g_autoptr(GTask) task = user_data; - LoadAsync *state; - - g_assert (SYSPROF_IS_MARKS_VIEW (view)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (G_IS_TASK (task)); - - state = g_task_get_task_data (task); - g_assert (state != NULL); - g_assert (state->reader != NULL); - g_assert (state->n_active > 0); - - state->n_active--; - - if (!sysprof_marks_view_load_finish (view, result, &error)) - { - if (!state->has_error) - { - state->has_error = TRUE; - g_task_return_error (task, g_steal_pointer (&error)); - } - - return; - } - - if (state->n_active == 0) - g_task_return_boolean (task, TRUE); -} - -static void -sysprof_capture_view_load_callgraph_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofCaptureView *self = (SysprofCaptureView *)object; - g_autoptr(GError) error = NULL; - g_autoptr(GTask) task = user_data; - LoadAsync *state; - - g_assert (SYSPROF_IS_CAPTURE_VIEW (self)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (G_IS_TASK (task)); - - state = g_task_get_task_data (task); - g_assert (state != NULL); - g_assert (state->reader != NULL); - g_assert (state->n_active > 0); - - state->n_active--; - - if (!sysprof_capture_view_generate_callgraph_finish (self, result, &error)) - { - if (!state->has_error) - { - state->has_error = TRUE; - g_task_return_error (task, g_steal_pointer (&error)); - } - - return; - } - - if (state->n_active == 0) - g_task_return_boolean (task, TRUE); -} - -static void -sysprof_capture_view_load_logs_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofLogsView *logs_view = (SysprofLogsView *)object; - g_autoptr(GError) error = NULL; - g_autoptr(GTask) task = user_data; - LoadAsync *state; - - g_assert (SYSPROF_IS_LOGS_VIEW (logs_view)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (G_IS_TASK (task)); - - state = g_task_get_task_data (task); - g_assert (state != NULL); - g_assert (state->reader != NULL); - g_assert (state->n_active > 0); - - if (!sysprof_logs_view_load_finish (logs_view, result, &error)) - g_warning ("Failed to load logs: %s", error->message); - - state->n_active--; - - if (state->n_active == 0) - g_task_return_boolean (task, TRUE); -} - -static void -sysprof_capture_view_load_scan_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofCaptureView *self = (SysprofCaptureView *)object; - SysprofCaptureViewPrivate *priv = sysprof_capture_view_get_instance_private (self); - g_autoptr(GTask) task = user_data; - g_autoptr(GError) error = NULL; - LoadAsync *state; - - g_assert (SYSPROF_IS_CAPTURE_VIEW (self)); - g_assert (G_IS_ASYNC_RESULT (result)); - g_assert (G_IS_TASK (task)); - - if (!sysprof_capture_view_scan_finish (self, result, &error)) - { - g_task_return_error (task, g_steal_pointer (&error)); - return; - } - - state = g_task_get_task_data (task); - g_assert (state != NULL); - g_assert (state->reader != NULL); - - if (priv->features.has_samples) - { - state->n_active++; - sysprof_capture_view_generate_callgraph_async (self, - state->reader, - state->selection, - g_task_get_cancellable (task), - sysprof_capture_view_load_callgraph_cb, - g_object_ref (task)); - } - - if (priv->features.has_logs) - { - state->n_active++; - sysprof_logs_view_load_async (priv->logs_view, - state->reader, - state->selection, - g_task_get_cancellable (task), - sysprof_capture_view_load_logs_cb, - g_object_ref (task)); - } - - sysprof_visualizer_view_set_reader (priv->visualizer_view, state->reader); - - if (priv->features.has_counters) - { - state->n_active++; - sysprof_marks_view_load_async (priv->counters_view, - state->reader, - state->selection, - g_task_get_cancellable (task), - sysprof_capture_view_load_marks_cb, - g_object_ref (task)); - } - - state->n_active++; - sysprof_marks_view_load_async (priv->marks_view, - state->reader, - state->selection, - g_task_get_cancellable (task), - sysprof_capture_view_load_marks_cb, - g_object_ref (task)); - - sysprof_details_view_set_reader (priv->details_view, priv->reader); - - if (state->n_active == 0) - g_task_return_boolean (task, TRUE); -} - -static void -sysprof_capture_view_real_load_async (SysprofCaptureView *self, - SysprofCaptureReader *reader, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - SysprofCaptureViewPrivate *priv = sysprof_capture_view_get_instance_private (self); - g_autoptr(GTask) task = NULL; - SysprofSelection *selection; - LoadAsync *state; - - g_assert (SYSPROF_IS_CAPTURE_VIEW (self)); - g_assert (reader != NULL); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - g_clear_pointer (&priv->reader, sysprof_capture_reader_unref); - priv->reader = sysprof_capture_reader_ref (reader); - - selection = sysprof_visualizer_view_get_selection (priv->visualizer_view); - - state = g_slice_new0 (LoadAsync); - state->reader = sysprof_capture_reader_copy (reader); - state->selection = g_object_ref (selection); - state->n_active = 0; - - task = g_task_new (self, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_capture_view_real_load_async); - g_task_set_task_data (task, - g_steal_pointer (&state), - load_async_free); - sysprof_capture_view_monitor_task (self, task); - - /* Zoom after loading state */ - g_signal_connect_object (task, - "notify::completed", - G_CALLBACK (sysprof_capture_view_fit_to_width), - self, - G_CONNECT_SWAPPED); - - /* Cancel any previously in-flight operations and save a cancellable so - * that any supplimental calls will result in the previous being cancelled. - */ - if (priv->cancellable != cancellable) - { - g_cancellable_cancel (priv->cancellable); - g_clear_object (&priv->cancellable); - g_set_object (&priv->cancellable, cancellable); - } - - /* First, discover the the time range for the display */ - sysprof_capture_view_scan_async (self, - reader, - cancellable, - sysprof_capture_view_load_scan_cb, - g_steal_pointer (&task)); -} - -static gboolean -sysprof_capture_view_real_load_finish (SysprofCaptureView *self, - GAsyncResult *result, - GError **error) -{ - g_assert (SYSPROF_IS_CAPTURE_VIEW (self)); - g_assert (G_IS_TASK (result)); - - return g_task_propagate_boolean (G_TASK (result), error); -} - -static void -sysprof_capture_view_selection_changed_cb (SysprofCaptureView *self, - SysprofSelection *selection) -{ - SysprofCaptureViewPrivate *priv = sysprof_capture_view_get_instance_private (self); - - g_assert (SYSPROF_IS_CAPTURE_VIEW (self)); - g_assert (SYSPROF_IS_SELECTION (selection)); - - if (priv->reader == NULL) - return; - - sysprof_capture_view_generate_callgraph_async (self, - priv->reader, - selection, - NULL, NULL, NULL); - sysprof_logs_view_load_async (priv->logs_view, - priv->reader, - selection, - NULL, NULL, NULL); - sysprof_marks_view_load_async (priv->marks_view, - priv->reader, - selection, - NULL, NULL, NULL); -} - -static void -fit_zoom_cb (GSimpleAction *action, - GVariant *param, - gpointer user_data) -{ - SysprofCaptureView *self = user_data; - - g_assert (G_IS_SIMPLE_ACTION (action)); - g_assert (SYSPROF_IS_CAPTURE_VIEW (self)); - - sysprof_capture_view_fit_to_width (self); -} - -static void -set_use_underline_cb (GtkWidget *widget, - gpointer user_data) -{ - if (GTK_IS_RADIO_BUTTON (widget)) - { - GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget)); - - if (GTK_IS_LABEL (child)) - gtk_label_set_use_underline (GTK_LABEL (child), TRUE); - } -} - -static void -sysprof_capture_view_map (GtkWidget *widget) -{ - SysprofCaptureView *self = (SysprofCaptureView *)widget; - SysprofCaptureViewPrivate *priv = sysprof_capture_view_get_instance_private (self); - - g_assert (SYSPROF_IS_CAPTURE_VIEW (self)); - - GTK_WIDGET_CLASS (sysprof_capture_view_parent_class)->map (widget); - - if (priv->needs_fit) - g_timeout_add_full (G_PRIORITY_LOW, - 25, - do_best_fit_in_idle, - g_object_ref (self), - g_object_unref); -} - -static void -sysprof_capture_view_finalize (GObject *object) -{ - SysprofCaptureView *self = (SysprofCaptureView *)object; - SysprofCaptureViewPrivate *priv = sysprof_capture_view_get_instance_private (self); - - g_clear_pointer (&priv->mark_stats, g_hash_table_unref); - g_clear_pointer (&priv->reader, sysprof_capture_reader_unref); - g_clear_object (&priv->cancellable); - - G_OBJECT_CLASS (sysprof_capture_view_parent_class)->finalize (object); -} - -static void -sysprof_capture_view_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofCaptureView *self = (SysprofCaptureView *)object; - - switch (prop_id) - { - case PROP_BUSY: - g_value_set_boolean (value, sysprof_capture_view_get_busy (self)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_capture_view_class_init (SysprofCaptureViewClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - object_class->finalize = sysprof_capture_view_finalize; - object_class->get_property = sysprof_capture_view_get_property; - - widget_class->map = sysprof_capture_view_map; - - klass->load_async = sysprof_capture_view_real_load_async; - klass->load_finish = sysprof_capture_view_real_load_finish; - - gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-capture-view.ui"); - gtk_widget_class_bind_template_child_private (widget_class, SysprofCaptureView, callgraph_view); - gtk_widget_class_bind_template_child_private (widget_class, SysprofCaptureView, details_view); - gtk_widget_class_bind_template_child_private (widget_class, SysprofCaptureView, counters_view); - gtk_widget_class_bind_template_child_private (widget_class, SysprofCaptureView, logs_view); - gtk_widget_class_bind_template_child_private (widget_class, SysprofCaptureView, marks_view); - gtk_widget_class_bind_template_child_private (widget_class, SysprofCaptureView, stack); - gtk_widget_class_bind_template_child_private (widget_class, SysprofCaptureView, stack_switcher); - gtk_widget_class_bind_template_child_private (widget_class, SysprofCaptureView, time_adj); - gtk_widget_class_bind_template_child_private (widget_class, SysprofCaptureView, visualizer_view); - gtk_widget_class_bind_template_child_private (widget_class, SysprofCaptureView, zoom_manager); - - properties [PROP_BUSY] = - g_param_spec_boolean ("busy", - "Busy", - "If the widget is busy", - FALSE, - (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); - - g_type_ensure (DZL_TYPE_MULTI_PANED); - g_type_ensure (SYSPROF_TYPE_DETAILS_VIEW); - g_type_ensure (SYSPROF_TYPE_LOGS_VIEW); - g_type_ensure (SYSPROF_TYPE_MARKS_VIEW); - g_type_ensure (SYSPROF_TYPE_CALLGRAPH_VIEW); -} - -static void -sysprof_capture_view_init (SysprofCaptureView *self) -{ - SysprofCaptureViewPrivate *priv = sysprof_capture_view_get_instance_private (self); - g_autoptr(GSimpleActionGroup) group = NULL; - SysprofSelection *selection; - static GActionEntry actions[] = { - { "fit-zoom", fit_zoom_cb }, - }; - - gtk_widget_init_template (GTK_WIDGET (self)); - - gtk_container_foreach (GTK_CONTAINER (priv->stack_switcher), - set_use_underline_cb, - NULL); - - selection = sysprof_visualizer_view_get_selection (priv->visualizer_view); - g_signal_connect_object (selection, - "changed", - G_CALLBACK (sysprof_capture_view_selection_changed_cb), - self, - G_CONNECT_SWAPPED); - - gtk_widget_insert_action_group (GTK_WIDGET (self), - "zoom", - G_ACTION_GROUP (priv->zoom_manager)); - - group = g_simple_action_group_new (); - g_action_map_add_action_entries (G_ACTION_MAP (group), - actions, - G_N_ELEMENTS (actions), - self); - gtk_widget_insert_action_group (GTK_WIDGET (self), - "capture-view", - G_ACTION_GROUP (group)); - - _sysprof_marks_view_set_hadjustment (priv->marks_view, priv->time_adj); - _sysprof_marks_view_set_hadjustment (priv->counters_view, priv->time_adj); - _sysprof_visualizer_view_set_hadjustment (priv->visualizer_view, priv->time_adj); -} - -/** - * sysprof_capture_view_load_async: - * @self: a #SysprofCaptureView - * - * Asynchronously loads a capture. - * - * Since: 3.34 - */ -void -sysprof_capture_view_load_async (SysprofCaptureView *self, - SysprofCaptureReader *reader, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - g_return_if_fail (SYSPROF_IS_CAPTURE_VIEW (self)); - g_return_if_fail (reader != NULL); - g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); - - SYSPROF_CAPTURE_VIEW_GET_CLASS (self)->load_async (self, reader, cancellable, callback, user_data); -} - -/** - * sysprof_capture_view_load_finish: - * @self: a #SysprofCaptureView - * - * Completes a request to load a capture. - * - * Since: 3.34 - */ -gboolean -sysprof_capture_view_load_finish (SysprofCaptureView *self, - GAsyncResult *result, - GError **error) -{ - g_return_val_if_fail (SYSPROF_IS_CAPTURE_VIEW (self), FALSE); - g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); - - return SYSPROF_CAPTURE_VIEW_GET_CLASS (self)->load_finish (self, result, error); -} - -/** - * sysprof_capture_view_get_busy: - * @self: a #SysprofCaptureView - * - * Returns: %TRUE if the view is busy loading capture contents - * - * Since: 3.34 - */ -gboolean -sysprof_capture_view_get_busy (SysprofCaptureView *self) -{ - SysprofCaptureViewPrivate *priv = sysprof_capture_view_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_CAPTURE_VIEW (self), FALSE); - - return priv->busy > 0; -} - -void -sysprof_capture_view_reset (SysprofCaptureView *self) -{ - g_return_if_fail (SYSPROF_IS_CAPTURE_VIEW (self)); - - /* TODO: reset */ - g_warning ("Clear all loaded state"); -} - -/** - * sysprof_capture_view_get_zoom_manager: - * @self: a #SysprofCaptureView - * - * Returns: (transfer none): a #SysprofZoomManager - * - * Since: 3.34 - */ -SysprofZoomManager * -sysprof_capture_view_get_zoom_manager (SysprofCaptureView *self) -{ - SysprofCaptureViewPrivate *priv = sysprof_capture_view_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_CAPTURE_VIEW (self), NULL); - - return priv->zoom_manager; -} - -void -sysprof_capture_view_fit_to_width (SysprofCaptureView *self) -{ - SysprofCaptureViewPrivate *priv = sysprof_capture_view_get_instance_private (self); - GtkWidget *toplevel; - GtkAllocation alloc; - gdouble zoom; - gint64 duration; - gint width; - - g_return_if_fail (SYSPROF_IS_CAPTURE_VIEW (self)); - - if (priv->reader == NULL) - return; - - duration = priv->features.end_time - priv->features.begin_time; - - if (duration <= 0) - return; - - priv->needs_fit = FALSE; - - gtk_widget_get_allocation (GTK_WIDGET (self), &alloc); - - /* If we got here before we got any sort of backing allocations - * created, then we will just try to guess the right size based - * on the window's default sizes. - */ - if ((toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self))) && - GTK_IS_WINDOW (toplevel) && - alloc.width <= 1) - g_object_get (toplevel, - "default-width", &alloc.width, - NULL); - - /* Trim a bit off the width to avoid drawing past edges */ - width = MAX (100, alloc.width - 25); - - zoom = sysprof_zoom_manager_fit_zoom_for_duration (priv->zoom_manager, duration, width); - sysprof_zoom_manager_set_zoom (priv->zoom_manager, zoom); -} - -/** - * sysprof_capture_view_get_reader: - * - * Gets the reader for the view, if any. - * - * Returns: (transfer none): a #SysprofCaptureReader or %NULL - * - * Since: 3.34 - */ -SysprofCaptureReader * -sysprof_capture_view_get_reader (SysprofCaptureView *self) -{ - SysprofCaptureViewPrivate *priv = sysprof_capture_view_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_CAPTURE_VIEW (self), NULL); - - return priv->reader; -} - -gboolean -sysprof_capture_view_get_can_replay (SysprofCaptureView *self) -{ - SysprofCaptureViewPrivate *priv = sysprof_capture_view_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_CAPTURE_VIEW (self), FALSE); - - return priv->features.can_replay; -} diff --git a/src/libsysprof-ui/sysprof-capture-view.h b/src/libsysprof-ui/sysprof-capture-view.h deleted file mode 100644 index dac9b10b..00000000 --- a/src/libsysprof-ui/sysprof-capture-view.h +++ /dev/null @@ -1,75 +0,0 @@ -/* sysprof-capture-view.h - * - * Copyright 2019 Christian Hergert - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -#pragma once - -#include -#include - -#include "sysprof-zoom-manager.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_CAPTURE_VIEW (sysprof_capture_view_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_DERIVABLE_TYPE (SysprofCaptureView, sysprof_capture_view, SYSPROF, CAPTURE_VIEW, GtkBin) - -struct _SysprofCaptureViewClass -{ - GtkBinClass parent_class; - - void (*load_async) (SysprofCaptureView *self, - SysprofCaptureReader *reader, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); - gboolean (*load_finish) (SysprofCaptureView *self, - GAsyncResult *result, - GError **error); - - /*< private >*/ - gpointer _reserved[32]; -}; - -SYSPROF_AVAILABLE_IN_ALL -GtkWidget *sysprof_capture_view_new (void); -SYSPROF_AVAILABLE_IN_ALL -SysprofZoomManager *sysprof_capture_view_get_zoom_manager (SysprofCaptureView *self); -SYSPROF_AVAILABLE_IN_ALL -SysprofCaptureReader *sysprof_capture_view_get_reader (SysprofCaptureView *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_capture_view_load_async (SysprofCaptureView *self, - SysprofCaptureReader *reader, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_capture_view_load_finish (SysprofCaptureView *self, - GAsyncResult *result, - GError **error); -SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_capture_view_get_busy (SysprofCaptureView *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_capture_view_fit_to_width (SysprofCaptureView *self); -SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_capture_view_get_can_replay (SysprofCaptureView *self); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-capture-view.ui b/src/libsysprof-ui/sysprof-capture-view.ui deleted file mode 100644 index ef0555bd..00000000 --- a/src/libsysprof-ui/sysprof-capture-view.ui +++ /dev/null @@ -1,187 +0,0 @@ - - - - - - 20 - - diff --git a/src/libsysprof-ui/sysprof-cell-renderer-duration.c b/src/libsysprof-ui/sysprof-cell-renderer-duration.c index 49c40171..462b83e8 100644 --- a/src/libsysprof-ui/sysprof-cell-renderer-duration.c +++ b/src/libsysprof-ui/sysprof-cell-renderer-duration.c @@ -110,7 +110,7 @@ sysprof_cell_renderer_duration_render (GtkCellRenderer *renderer, if (r.width > 3) { - _sysprof_rounded_rectangle (cr, &r, 2, 2); + dzl_cairo_rounded_rectangle (cr, &r, 2, 2); cairo_fill (cr); } else if (r.width > 1) diff --git a/src/libsysprof-ui/sysprof-cell-renderer-percent.c b/src/libsysprof-ui/sysprof-cell-renderer-percent.c index 97aa0a0d..d0d25d6d 100644 --- a/src/libsysprof-ui/sysprof-cell-renderer-percent.c +++ b/src/libsysprof-ui/sysprof-cell-renderer-percent.c @@ -18,6 +18,8 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +#define G_LOG_DOMAIN "sysprof-cell-renderer-percent" + #include "config.h" #include diff --git a/src/libsysprof-ui/sysprof-cell-renderer-percent.h b/src/libsysprof-ui/sysprof-cell-renderer-percent.h index 9c43720b..b917ea7f 100644 --- a/src/libsysprof-ui/sysprof-cell-renderer-percent.h +++ b/src/libsysprof-ui/sysprof-cell-renderer-percent.h @@ -20,14 +20,8 @@ #pragma once -#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION) -# error "Only can be included directly." -#endif - #include -#include "sysprof-version-macros.h" - G_BEGIN_DECLS #define SYSPROF_TYPE_CELL_RENDERER_PERCENT (sysprof_cell_renderer_percent_get_type()) @@ -50,16 +44,13 @@ struct _SysprofCellRendererPercentClass { GtkCellRendererProgressClass parent_class; - gpointer padding[4]; + /*< private >*/ + gpointer _reserved[4]; }; -SYSPROF_AVAILABLE_IN_ALL GType sysprof_cell_renderer_percent_get_type (void); -SYSPROF_AVAILABLE_IN_ALL GtkCellRenderer *sysprof_cell_renderer_percent_new (void); -SYSPROF_AVAILABLE_IN_ALL gdouble sysprof_cell_renderer_percent_get_percent (SysprofCellRendererPercent *self); -SYSPROF_AVAILABLE_IN_ALL void sysprof_cell_renderer_percent_set_percent (SysprofCellRendererPercent *self, gdouble percent); diff --git a/src/libsysprof-ui/sysprof-color-cycle.c b/src/libsysprof-ui/sysprof-color-cycle.c index d1c63c56..582878e9 100644 --- a/src/libsysprof-ui/sysprof-color-cycle.c +++ b/src/libsysprof-ui/sysprof-color-cycle.c @@ -27,30 +27,47 @@ G_DEFINE_BOXED_TYPE (SysprofColorCycle, sysprof_color_cycle, sysprof_color_cycle_ref, sysprof_color_cycle_unref) static const gchar *default_colors[] = { - "#73d216", - "#f57900", - "#3465a4", - "#ef2929", - "#75507b", - "#ce5c00", - "#c17d11", - "#cc0000", - "#edd400", - "#555753", - "#4e9a06", - "#204a87", - "#5c3566", - "#a40000", - "#c4a000", - "#8f5902", - "#2e3436", - "#8ae234", - "#729fcf", - "#ad7fa8", - "#fce94f", - "#fcaf3e", - "#e9b96e", - "#888a85", + + "#1a5fb4", /* Blue 5 */ + "#26a269", /* Green 5 */ + "#e5a50a", /* Yellow 5 */ + "#c64600", /* Orange 5 */ + "#a51d2d", /* Red 5 */ + "#613583", /* Purple 5 */ + "#63452c", /* Brown 5 */ + + "#1c71d8", /* Blue 4 */ + "#2ec27e", /* Green 4 */ + "#f5c211", /* Yellow 4 */ + "#e66100", /* Orange 4 */ + "#c01c28", /* Red 4 */ + "#813d9c", /* Purple 4 */ + "#865e3c", /* Brown 4 */ + + "#3584e4", /* Blue 3 */ + "#33d17a", /* Green 3 */ + "#f6d32d", /* Yellow 3 */ + "#ff7800", /* Orange 3 */ + "#e01b24", /* Red 3 */ + "#9141ac", /* Purple 3 */ + "#986a44", /* Brown 3 */ + + "#62a0ea", /* Blue 2 */ + "#57e389", /* Green 2 */ + "#f8e45c", /* Yellow 2 */ + "#ffa348", /* Orange 2 */ + "#ed333b", /* Red 2 */ + "#c061cb", /* Purple 2 */ + "#b5835a", /* Brown 2 */ + + "#99c1f1", /* Blue 1 */ + "#8ff0a4", /* Green 1 */ + "#f9f06b", /* Yellow 1 */ + "#ffbe6f", /* Orange 1 */ + "#f66151", /* Red 1 */ + "#dc8add", /* Purple 1 */ + "#cdab8f", /* Brown 1 */ + NULL }; diff --git a/src/libsysprof-ui/sysprof-color-cycle.h b/src/libsysprof-ui/sysprof-color-cycle.h index b512eeda..650acb78 100644 --- a/src/libsysprof-ui/sysprof-color-cycle.h +++ b/src/libsysprof-ui/sysprof-color-cycle.h @@ -20,32 +20,22 @@ #pragma once -#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION) -# error "Only can be included directly." -#endif - #include -#include "sysprof-version-macros.h" - G_BEGIN_DECLS #define SYSPROF_TYPE_COLOR_CYCLE (sysprof_color_cycle_get_type()) typedef struct _SysprofColorCycle SysprofColorCycle; -SYSPROF_AVAILABLE_IN_ALL GType sysprof_color_cycle_get_type (void); -SYSPROF_AVAILABLE_IN_ALL SysprofColorCycle *sysprof_color_cycle_ref (SysprofColorCycle *self); -SYSPROF_AVAILABLE_IN_ALL void sysprof_color_cycle_unref (SysprofColorCycle *self); -SYSPROF_AVAILABLE_IN_ALL SysprofColorCycle *sysprof_color_cycle_new (void); -SYSPROF_AVAILABLE_IN_ALL void sysprof_color_cycle_reset (SysprofColorCycle *self); -SYSPROF_AVAILABLE_IN_ALL void sysprof_color_cycle_next (SysprofColorCycle *self, GdkRGBA *rgba); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (SysprofColorCycle, sysprof_color_cycle_unref) + G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-counters-aid.c b/src/libsysprof-ui/sysprof-counters-aid.c new file mode 100644 index 00000000..86a9f271 --- /dev/null +++ b/src/libsysprof-ui/sysprof-counters-aid.c @@ -0,0 +1,248 @@ +/* sysprof-counters-aid.c + * + * Copyright 2019 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#define G_LOG_DOMAIN "sysprof-counters-aid" + +#include "config.h" + +#include + +#include "sysprof-color-cycle.h" +#include "sysprof-counters-aid.h" +#include "sysprof-line-visualizer.h" +#include "sysprof-marks-page.h" +#include "sysprof-time-visualizer.h" + +struct _SysprofCountersAid +{ + SysprofAid parent_instance; +}; + +typedef struct +{ + SysprofCaptureCursor *cursor; + SysprofDisplay *display; +} Present; + +G_DEFINE_TYPE (SysprofCountersAid, sysprof_counters_aid, SYSPROF_TYPE_AID) + +static void +present_free (gpointer data) +{ + Present *p = data; + + g_clear_pointer (&p->cursor, sysprof_capture_cursor_unref); + g_clear_object (&p->display); + g_slice_free (Present, p); +} + +static void +on_group_activated_cb (SysprofVisualizerGroup *group, + SysprofPage *page) +{ + SysprofDisplay *display; + + g_assert (SYSPROF_IS_VISUALIZER_GROUP (group)); + g_assert (SYSPROF_IS_PAGE (page)); + + display = SYSPROF_DISPLAY (gtk_widget_get_ancestor (GTK_WIDGET (page), SYSPROF_TYPE_DISPLAY)); + sysprof_display_set_visible_page (display, page); +} + +/** + * sysprof_counters_aid_new: + * + * Create a new #SysprofCountersAid. + * + * Returns: (transfer full): a newly created #SysprofCountersAid + * + * Since: 3.34 + */ +SysprofAid * +sysprof_counters_aid_new (void) +{ + return g_object_new (SYSPROF_TYPE_COUNTERS_AID, NULL); +} + +static void +sysprof_counters_aid_prepare (SysprofAid *self, + SysprofProfiler *profiler) +{ +} + +static gboolean +collect_counters (const SysprofCaptureFrame *frame, + gpointer user_data) +{ + SysprofCaptureCounterDefine *def = (SysprofCaptureCounterDefine *)frame; + GArray *counters = user_data; + + g_assert (frame != NULL); + g_assert (frame->type == SYSPROF_CAPTURE_FRAME_CTRDEF); + g_assert (counters != NULL); + + if (def->n_counters > 0) + g_array_append_vals (counters, def->counters, def->n_counters); + + return TRUE; +} + +static void +sysprof_counters_aid_present_worker (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + Present *present = task_data; + g_autoptr(GArray) counters = NULL; + + g_assert (G_IS_TASK (task)); + g_assert (SYSPROF_IS_COUNTERS_AID (source_object)); + g_assert (present != NULL); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + counters = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounter)); + sysprof_capture_cursor_foreach (present->cursor, collect_counters, counters); + g_task_return_pointer (task, + g_steal_pointer (&counters), + (GDestroyNotify) g_array_unref); +} + +static void +sysprof_counters_aid_present_async (SysprofAid *aid, + SysprofCaptureReader *reader, + SysprofDisplay *display, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + static const SysprofCaptureFrameType types[] = { SYSPROF_CAPTURE_FRAME_CTRDEF }; + g_autoptr(SysprofCaptureCondition) condition = NULL; + g_autoptr(SysprofCaptureCursor) cursor = NULL; + g_autoptr(GTask) task = NULL; + Present present; + + g_assert (SYSPROF_IS_COUNTERS_AID (aid)); + g_assert (reader != NULL); + g_assert (SYSPROF_IS_DISPLAY (display)); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + condition = sysprof_capture_condition_new_where_type_in (1, types); + cursor = sysprof_capture_cursor_new (reader); + sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition)); + + present.cursor = g_steal_pointer (&cursor); + present.display = g_object_ref (display); + + task = g_task_new (aid, cancellable, callback, user_data); + g_task_set_source_tag (task, sysprof_counters_aid_present_async); + g_task_set_task_data (task, + g_slice_dup (Present, &present), + present_free); + g_task_run_in_thread (task, sysprof_counters_aid_present_worker); +} + +static gboolean +sysprof_counters_aid_present_finish (SysprofAid *aid, + GAsyncResult *result, + GError **error) +{ + g_autoptr(GArray) counters = NULL; + Present *present; + + g_assert (SYSPROF_IS_AID (aid)); + g_assert (G_IS_TASK (result)); + + present = g_task_get_task_data (G_TASK (result)); + + if ((counters = g_task_propagate_pointer (G_TASK (result), error)) && counters->len > 0) + { + g_autoptr(SysprofColorCycle) cycle = sysprof_color_cycle_new (); + SysprofVisualizerGroup *group; + SysprofVisualizer *combined; + GtkWidget *page; + + group = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP, + "can-focus", TRUE, + "has-page", TRUE, + "title", _("Counters"), + "visible", TRUE, + NULL); + + combined = g_object_new (SYSPROF_TYPE_TIME_VISUALIZER, + "title", _("Counters"), + "height-request", 35, + "visible", TRUE, + NULL); + sysprof_visualizer_group_insert (group, combined, -1, TRUE); + + for (guint i = 0; i < counters->len; i++) + { + const SysprofCaptureCounter *ctr = &g_array_index (counters, SysprofCaptureCounter, i); + g_autofree gchar *title = g_strdup_printf ("%s — %s", ctr->category, ctr->name); + GtkWidget *row; + GdkRGBA rgba; + + row = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER, + "title", title, + "height-request", 35, + "visible", FALSE, + NULL); + sysprof_color_cycle_next (cycle, &rgba); + sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba); + rgba.alpha = .5; + sysprof_line_visualizer_set_fill (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba); + sysprof_time_visualizer_add_counter (SYSPROF_TIME_VISUALIZER (combined), ctr->id, &rgba); + sysprof_visualizer_group_insert (group, SYSPROF_VISUALIZER (row), -1, TRUE); + } + + sysprof_display_add_group (present->display, group); + + page = sysprof_marks_page_new (sysprof_display_get_zoom_manager (present->display), + SYSPROF_MARKS_MODEL_COUNTERS); + gtk_widget_show (page); + + g_signal_connect_object (group, + "group-activated", + G_CALLBACK (on_group_activated_cb), + page, + 0); + sysprof_display_add_page (present->display, SYSPROF_PAGE (page)); + } + + return counters != NULL; +} + +static void +sysprof_counters_aid_class_init (SysprofCountersAidClass *klass) +{ + SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass); + + aid_class->prepare = sysprof_counters_aid_prepare; + aid_class->present_async = sysprof_counters_aid_present_async; + aid_class->present_finish = sysprof_counters_aid_present_finish; +} + +static void +sysprof_counters_aid_init (SysprofCountersAid *self) +{ + sysprof_aid_set_display_name (SYSPROF_AID (self), _("Battery")); + sysprof_aid_set_icon_name (SYSPROF_AID (self), "org.gnome.Sysprof3-symbolic"); +} diff --git a/src/libsysprof-ui/sysprof-counters-aid.h b/src/libsysprof-ui/sysprof-counters-aid.h new file mode 100644 index 00000000..5541b60f --- /dev/null +++ b/src/libsysprof-ui/sysprof-counters-aid.h @@ -0,0 +1,33 @@ +/* sysprof-counters-aid.h + * + * Copyright 2019 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-aid.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_COUNTERS_AID (sysprof_counters_aid_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofCountersAid, sysprof_counters_aid, SYSPROF, COUNTERS_AID, SysprofAid) + +SysprofAid *sysprof_counters_aid_new (void); + +G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-cpu-aid.c b/src/libsysprof-ui/sysprof-cpu-aid.c index 22cd05e6..31d913f3 100644 --- a/src/libsysprof-ui/sysprof-cpu-aid.c +++ b/src/libsysprof-ui/sysprof-cpu-aid.c @@ -24,15 +24,33 @@ #include +#include "sysprof-color-cycle.h" #include "sysprof-cpu-aid.h" +#include "sysprof-line-visualizer.h" struct _SysprofCpuAid { SysprofAid parent_instance; }; +typedef struct +{ + SysprofCaptureCursor *cursor; + SysprofDisplay *display; +} Present; + G_DEFINE_TYPE (SysprofCpuAid, sysprof_cpu_aid, SYSPROF_TYPE_AID) +static void +present_free (gpointer data) +{ + Present *p = data; + + g_clear_pointer (&p->cursor, sysprof_capture_cursor_unref); + g_clear_object (&p->display); + g_slice_free (Present, p); +} + /** * sysprof_cpu_aid_new: * @@ -63,12 +81,243 @@ sysprof_cpu_aid_prepare (SysprofAid *self, #endif } +static gboolean +collect_cpu_counters (const SysprofCaptureFrame *frame, + gpointer user_data) +{ + SysprofCaptureCounterDefine *def = (SysprofCaptureCounterDefine *)frame; + GArray *counters = user_data; + + g_assert (frame != NULL); + g_assert (frame->type == SYSPROF_CAPTURE_FRAME_CTRDEF); + g_assert (counters != NULL); + + for (guint i = 0; i < def->n_counters; i++) + { + const SysprofCaptureCounter *counter = &def->counters[i]; + + if (g_strcmp0 (counter->category, "CPU Percent") == 0 || + g_strcmp0 (counter->category, "CPU Frequency") == 0) + g_array_append_vals (counters, counter, 1); + } + + return TRUE; +} + +static void +sysprof_cpu_aid_present_worker (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + Present *present = task_data; + g_autoptr(GArray) counters = NULL; + + g_assert (G_IS_TASK (task)); + g_assert (SYSPROF_IS_CPU_AID (source_object)); + g_assert (present != NULL); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + counters = g_array_new (FALSE, FALSE, sizeof (SysprofCaptureCounter)); + sysprof_capture_cursor_foreach (present->cursor, collect_cpu_counters, counters); + g_task_return_pointer (task, + g_steal_pointer (&counters), + (GDestroyNotify) g_array_unref); +} + +static void +sysprof_cpu_aid_present_async (SysprofAid *aid, + SysprofCaptureReader *reader, + SysprofDisplay *display, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + static const SysprofCaptureFrameType types[] = { SYSPROF_CAPTURE_FRAME_CTRDEF }; + g_autoptr(SysprofCaptureCondition) condition = NULL; + g_autoptr(SysprofCaptureCursor) cursor = NULL; + g_autoptr(GTask) task = NULL; + Present present; + + g_assert (SYSPROF_IS_CPU_AID (aid)); + g_assert (reader != NULL); + g_assert (SYSPROF_IS_DISPLAY (display)); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + condition = sysprof_capture_condition_new_where_type_in (1, types); + cursor = sysprof_capture_cursor_new (reader); + sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition)); + + present.cursor = g_steal_pointer (&cursor); + present.display = g_object_ref (display); + + task = g_task_new (aid, cancellable, callback, user_data); + g_task_set_source_tag (task, sysprof_cpu_aid_present_async); + g_task_set_task_data (task, + g_slice_dup (Present, &present), + present_free); + g_task_run_in_thread (task, sysprof_cpu_aid_present_worker); +} + +static gboolean +sysprof_cpu_aid_present_finish (SysprofAid *aid, + GAsyncResult *result, + GError **error) +{ + g_autoptr(GArray) counters = NULL; + Present *present; + + g_assert (SYSPROF_IS_AID (aid)); + g_assert (G_IS_TASK (result)); + + present = g_task_get_task_data (G_TASK (result)); + + if ((counters = g_task_propagate_pointer (G_TASK (result), error))) + { + g_autoptr(SysprofColorCycle) cycle = sysprof_color_cycle_new (); + g_autoptr(SysprofColorCycle) freq_cycle = sysprof_color_cycle_new (); + SysprofVisualizerGroup *usage; + SysprofVisualizerGroup *freq; + SysprofVisualizer *freq_row = NULL; + SysprofVisualizer *over_row = NULL; + gboolean found_combined = FALSE; + gboolean has_usage = FALSE; + gboolean has_freq = FALSE; + + usage = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP, + "can-focus", TRUE, + "priority", -1000, + "title", _("CPU Usage"), + "visible", TRUE, + NULL); + + freq = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP, + "can-focus", TRUE, + "priority", -999, + "title", _("CPU Frequency"), + "visible", TRUE, + NULL); + freq_row = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER, + "title", _("CPU Frequency (All)"), + "height-request", 35, + "visible", TRUE, + "y-lower", 0.0, + "y-upper", 100.0, + NULL); + gtk_container_add (GTK_CONTAINER (freq), GTK_WIDGET (freq_row)); + + over_row = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER, + "title", _("CPU Usage (All)"), + "height-request", 35, + "visible", TRUE, + "y-lower", 0.0, + "y-upper", 100.0, + NULL); + + for (guint i = 0; i < counters->len; i++) + { + const SysprofCaptureCounter *ctr = &g_array_index (counters, SysprofCaptureCounter, i); + + if (g_strcmp0 (ctr->category, "CPU Percent") == 0) + { + if (strstr (ctr->name, "Combined") != NULL) + { + GtkWidget *row; + GdkRGBA rgba; + + found_combined = TRUE; + + gdk_rgba_parse (&rgba, "#1a5fb4"); + row = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER, + /* Translators: CPU is the processor. */ + "title", _("CPU Usage (All)"), + "height-request", 35, + "visible", TRUE, + "y-lower", 0.0, + "y-upper", 100.0, + NULL); + sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba); + rgba.alpha = 0.5; + sysprof_line_visualizer_set_fill (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba); + sysprof_visualizer_group_insert (usage, SYSPROF_VISUALIZER (row), 0, FALSE); + has_usage = TRUE; + } + else if (g_str_has_prefix (ctr->name, "Total CPU ")) + { + GtkWidget *row; + GdkRGBA rgba; + + sysprof_color_cycle_next (cycle, &rgba); + row = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER, + "title", ctr->name, + "height-request", 35, + "visible", FALSE, + "y-lower", 0.0, + "y-upper", 100.0, + NULL); + sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba); + sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (over_row), ctr->id, &rgba); + rgba.alpha = 0.5; + sysprof_line_visualizer_set_fill (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba); + sysprof_visualizer_group_insert (usage, SYSPROF_VISUALIZER (row), -1, TRUE); + has_usage = TRUE; + } + } + else if (g_strcmp0 (ctr->category, "CPU Frequency") == 0) + { + if (g_str_has_prefix (ctr->name, "CPU ")) + { + g_autofree gchar *title = g_strdup_printf ("%s Frequency", ctr->name); + GtkWidget *row; + GdkRGBA rgba; + + sysprof_color_cycle_next (freq_cycle, &rgba); + sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (freq_row), ctr->id, &rgba); + sysprof_line_visualizer_set_dash (SYSPROF_LINE_VISUALIZER (freq_row), ctr->id, TRUE); + + row = g_object_new (SYSPROF_TYPE_LINE_VISUALIZER, + "title", title, + "height-request", 35, + "visible", FALSE, + "y-lower", 0.0, + "y-upper", 100.0, + NULL); + sysprof_line_visualizer_add_counter (SYSPROF_LINE_VISUALIZER (row), ctr->id, &rgba); + sysprof_line_visualizer_set_dash (SYSPROF_LINE_VISUALIZER (row), ctr->id, TRUE); + sysprof_visualizer_group_insert (freq, SYSPROF_VISUALIZER (row), -1, TRUE); + + has_freq = TRUE; + } + } + } + + if (has_usage && !found_combined) + sysprof_visualizer_group_insert (usage, over_row, 0, FALSE); + else + gtk_widget_destroy (GTK_WIDGET (over_row)); + + if (has_usage) + sysprof_display_add_group (present->display, usage); + else + gtk_widget_destroy (GTK_WIDGET (usage)); + + if (has_freq) + sysprof_display_add_group (present->display, freq); + else + gtk_widget_destroy (GTK_WIDGET (freq)); + } + + return counters != NULL; +} + static void sysprof_cpu_aid_class_init (SysprofCpuAidClass *klass) { SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass); aid_class->prepare = sysprof_cpu_aid_prepare; + aid_class->present_async = sysprof_cpu_aid_present_async; + aid_class->present_finish = sysprof_cpu_aid_present_finish; } static void diff --git a/src/libsysprof-ui/sysprof-cpu-aid.h b/src/libsysprof-ui/sysprof-cpu-aid.h index ca5899bc..4769885e 100644 --- a/src/libsysprof-ui/sysprof-cpu-aid.h +++ b/src/libsysprof-ui/sysprof-cpu-aid.h @@ -26,10 +26,8 @@ G_BEGIN_DECLS #define SYSPROF_TYPE_CPU_AID (sysprof_cpu_aid_get_type()) -SYSPROF_AVAILABLE_IN_ALL G_DECLARE_FINAL_TYPE (SysprofCpuAid, sysprof_cpu_aid, SYSPROF, CPU_AID, SysprofAid) -SYSPROF_AVAILABLE_IN_ALL SysprofAid *sysprof_cpu_aid_new (void); G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-cpu-visualizer-row.c b/src/libsysprof-ui/sysprof-cpu-visualizer-row.c deleted file mode 100644 index 789bfe65..00000000 --- a/src/libsysprof-ui/sysprof-cpu-visualizer-row.c +++ /dev/null @@ -1,239 +0,0 @@ -/* sysprof-cpu-visualizer-row.c - * - * Copyright 2016-2019 Christian Hergert - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -#define G_LOG_DOMAIN "sysprof-cpu-visualizer-row" - -#include "config.h" - -#include - -#include "sysprof-color-cycle.h" -#include "sysprof-cpu-visualizer-row.h" - -struct _SysprofCpuVisualizerRow -{ - SysprofLineVisualizerRow parent_instance; - SysprofColorCycle *colors; - gchar *category; - guint use_dash : 1; -}; - -enum { - PROP_0, - PROP_CATEGORY, - PROP_USE_DASH, - N_PROPS -}; - -static GParamSpec *properties [N_PROPS]; - -G_DEFINE_TYPE (SysprofCpuVisualizerRow, sysprof_cpu_visualizer_row, SYSPROF_TYPE_LINE_VISUALIZER_ROW) - -static gboolean -sysprof_cpu_visualizer_counter_found (const SysprofCaptureFrame *frame, - gpointer user_data) -{ - const SysprofCaptureCounterDefine *def = (SysprofCaptureCounterDefine *)frame; - struct { - SysprofCpuVisualizerRow *self; - GArray *counters; - } *state = user_data; - gboolean found = FALSE; - - g_assert (frame->type == SYSPROF_CAPTURE_FRAME_CTRDEF); - g_assert (state != NULL); - - /* - * In practice, all the CPU counters are defined at once, so we can avoid - * walking the rest of the capture by returning after we find our CTRDEF. - */ - - for (guint i = 0; i < def->n_counters; i++) - { - if (strcmp (def->counters[i].category, state->self->category) == 0 && - strstr (def->counters[i].name, "(Combined)") == NULL) - { - guint id = def->counters[i].id; - g_array_append_val (state->counters, id); - found = TRUE; - } - } - - return !found; -} - -static void -sysprof_cpu_visualizer_row_discover_counters (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *canellable) -{ - const SysprofCaptureFrameType types[] = { SYSPROF_CAPTURE_FRAME_CTRDEF }; - SysprofCaptureReader *reader = task_data; - g_autoptr(SysprofCaptureCursor) cursor = NULL; - g_autoptr(GArray) counters = NULL; - struct { - SysprofCpuVisualizerRow *self; - GArray *counters; - } state; - - g_assert (G_IS_TASK (task)); - g_assert (SYSPROF_IS_CPU_VISUALIZER_ROW (source_object)); - g_assert (reader != NULL); - - counters = g_array_new (FALSE, FALSE, sizeof (guint)); - - state.self = source_object; - state.counters = counters; - - cursor = sysprof_capture_cursor_new (reader); - sysprof_capture_cursor_add_condition (cursor, sysprof_capture_condition_new_where_type_in (G_N_ELEMENTS (types), types)); - sysprof_capture_cursor_foreach (cursor, sysprof_cpu_visualizer_counter_found, &state); - g_task_return_pointer (task, g_steal_pointer (&counters), (GDestroyNotify)g_array_unref); -} - -static void -complete_counters (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofCpuVisualizerRow *self = (SysprofCpuVisualizerRow *)object; - g_autoptr(GArray) counters = NULL; - - g_assert (SYSPROF_IS_CPU_VISUALIZER_ROW (self)); - g_assert (G_IS_TASK (result)); - - counters = g_task_propagate_pointer (G_TASK (result), NULL); - - if (counters != NULL) - { - for (guint i = 0; i < counters->len; i++) - { - guint counter_id = g_array_index (counters, guint, i); - GdkRGBA color; - - sysprof_color_cycle_next (self->colors, &color); - sysprof_line_visualizer_row_add_counter (SYSPROF_LINE_VISUALIZER_ROW (self), counter_id, &color); - - if (self->use_dash) - sysprof_line_visualizer_row_set_dash (SYSPROF_LINE_VISUALIZER_ROW (self), counter_id, TRUE); - } - } - - /* Hide ourself if we failed to locate counters */ - gtk_widget_set_visible (GTK_WIDGET (self), counters != NULL && counters->len > 0); -} - -static void -sysprof_cpu_visualizer_row_set_reader (SysprofVisualizerRow *row, - SysprofCaptureReader *reader) -{ - SysprofCpuVisualizerRow *self = (SysprofCpuVisualizerRow *)row; - g_autoptr(GTask) task = NULL; - - g_assert (SYSPROF_IS_CPU_VISUALIZER_ROW (row)); - - sysprof_color_cycle_reset (self->colors); - - sysprof_line_visualizer_row_clear (SYSPROF_LINE_VISUALIZER_ROW (row)); - - SYSPROF_VISUALIZER_ROW_CLASS (sysprof_cpu_visualizer_row_parent_class)->set_reader (row, reader); - - if (reader != NULL) - { - task = g_task_new (self, NULL, complete_counters, NULL); - g_task_set_source_tag (task, sysprof_cpu_visualizer_row_set_reader); - g_task_set_task_data (task, sysprof_capture_reader_copy (reader), - (GDestroyNotify)sysprof_capture_reader_unref); - g_task_run_in_thread (task, sysprof_cpu_visualizer_row_discover_counters); - } -} - -static void -sysprof_cpu_visualizer_row_finalize (GObject *object) -{ - SysprofCpuVisualizerRow *self = (SysprofCpuVisualizerRow *)object; - - g_clear_pointer (&self->colors, sysprof_color_cycle_unref); - g_clear_pointer (&self->category, g_free); - - G_OBJECT_CLASS (sysprof_cpu_visualizer_row_parent_class)->finalize (object); -} - -static void -sysprof_cpu_visualizer_row_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofCpuVisualizerRow *self = SYSPROF_CPU_VISUALIZER_ROW (object); - - switch (prop_id) - { - case PROP_CATEGORY: - g_free (self->category); - self->category = g_value_dup_string (value); - break; - - case PROP_USE_DASH: - self->use_dash = g_value_get_boolean (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_cpu_visualizer_row_class_init (SysprofCpuVisualizerRowClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - SysprofVisualizerRowClass *row_class = SYSPROF_VISUALIZER_ROW_CLASS (klass); - - object_class->finalize = sysprof_cpu_visualizer_row_finalize; - object_class->set_property = sysprof_cpu_visualizer_row_set_property; - - row_class->set_reader = sysprof_cpu_visualizer_row_set_reader; - - properties [PROP_CATEGORY] = - g_param_spec_string ("category", NULL, NULL, - "CPU Percent", - (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - properties [PROP_USE_DASH] = - g_param_spec_boolean ("use-dash", NULL, NULL, - FALSE, - (G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); -} - -static void -sysprof_cpu_visualizer_row_init (SysprofCpuVisualizerRow *self) -{ - self->category = g_strdup ("CPU Percent"); - self->colors = sysprof_color_cycle_new (); -} - -GtkWidget * -sysprof_cpu_visualizer_row_new (void) -{ - return g_object_new (SYSPROF_TYPE_CPU_VISUALIZER_ROW, NULL); -} diff --git a/src/libsysprof-ui/sysprof-depth-visualizer-row.c b/src/libsysprof-ui/sysprof-depth-visualizer-row.c deleted file mode 100644 index 0353e040..00000000 --- a/src/libsysprof-ui/sysprof-depth-visualizer-row.c +++ /dev/null @@ -1,357 +0,0 @@ -/* sysprof-depth-visualizer-row.c - * - * Copyright 2019 Christian Hergert - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -#define G_LOG_DOMAIN "sysprof-depth-visualizer-row" - -#include "config.h" - -#include - -#include "pointcache.h" -#include "sysprof-depth-visualizer-row.h" - -struct _SysprofDepthVisualizerRow -{ - SysprofVisualizerRow parent_instance; - SysprofCaptureReader *reader; - PointCache *points; - guint reload_source; -}; - -typedef struct -{ - SysprofCaptureReader *reader; - PointCache *pc; - gint64 begin_time; - gint64 end_time; - gint64 duration; - guint max_n_addrs; -} State; - -G_DEFINE_TYPE (SysprofDepthVisualizerRow, sysprof_depth_visualizer_row, SYSPROF_TYPE_VISUALIZER_ROW) - -static void -state_free (State *st) -{ - g_clear_pointer (&st->reader, sysprof_capture_reader_unref); - g_clear_pointer (&st->pc, point_cache_unref); - g_slice_free (State, st); -} - -static gboolean -discover_max_n_addr (const SysprofCaptureFrame *frame, - gpointer user_data) -{ - const SysprofCaptureSample *sample = (const SysprofCaptureSample *)frame; - State *st = user_data; - - g_assert (frame != NULL); - g_assert (frame->type == SYSPROF_CAPTURE_FRAME_SAMPLE); - g_assert (st != NULL); - - st->max_n_addrs = MAX (st->max_n_addrs, sample->n_addrs); - - return TRUE; -} - -static gboolean -build_point_cache_cb (const SysprofCaptureFrame *frame, - gpointer user_data) -{ - const SysprofCaptureSample *sample = (const SysprofCaptureSample *)frame; - State *st = user_data; - gdouble x, y; - - g_assert (frame != NULL); - g_assert (frame->type == SYSPROF_CAPTURE_FRAME_SAMPLE); - g_assert (st != NULL); - - x = (frame->time - st->begin_time) / (gdouble)st->duration; - y = sample->n_addrs / (gdouble)st->max_n_addrs; - - /* If this contains a context-switch (meaning we're going into the kernel - * to do some work, use a negative value for Y so that we know later on - * that we should draw it with a different color (after removing the negation - * on the value. - * - * We skip past the first index, which is always a context switch as it is - * our perf handler. - */ - for (guint i = 1; i < sample->n_addrs; i++) - { - SysprofAddressContext kind; - - if (sysprof_address_is_context_switch (sample->addrs[i], &kind)) - { - y = -y; - break; - } - } - - point_cache_add_point_to_set (st->pc, 1, x, y); - - return TRUE; -} - -static void -sysprof_depth_visualizer_row_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - static const SysprofCaptureFrameType types[] = { SYSPROF_CAPTURE_FRAME_SAMPLE, }; - g_autoptr(SysprofCaptureCursor) cursor = NULL; - SysprofCaptureCondition *condition; - State *st = task_data; - - g_assert (G_IS_TASK (task)); - g_assert (SYSPROF_IS_DEPTH_VISUALIZER_ROW (source_object)); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - if (st->duration != 0) - { - cursor = sysprof_capture_cursor_new (st->reader); - condition = sysprof_capture_condition_new_where_type_in (G_N_ELEMENTS (types), types); - sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition)); - - sysprof_capture_cursor_foreach (cursor, discover_max_n_addr, st); - sysprof_capture_cursor_reset (cursor); - sysprof_capture_cursor_foreach (cursor, build_point_cache_cb, st); - } - - g_task_return_pointer (task, - g_steal_pointer (&st->pc), - (GDestroyNotify) point_cache_unref); -} - -static void -apply_point_cache_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofDepthVisualizerRow *self = (SysprofDepthVisualizerRow *)object; - PointCache *pc; - - g_assert (SYSPROF_IS_DEPTH_VISUALIZER_ROW (self)); - g_assert (G_IS_TASK (result)); - - if ((pc = g_task_propagate_pointer (G_TASK (result), NULL))) - { - g_clear_pointer (&self->points, point_cache_unref); - self->points = g_steal_pointer (&pc); - gtk_widget_queue_draw (GTK_WIDGET (self)); - } -} - -static void -sysprof_depth_visualizer_row_reload (SysprofDepthVisualizerRow *self) -{ - g_autoptr(GTask) task = NULL; - GtkAllocation alloc; - State *st; - - g_assert (SYSPROF_IS_DEPTH_VISUALIZER_ROW (self)); - - gtk_widget_get_allocation (GTK_WIDGET (self), &alloc); - - st = g_slice_new0 (State); - st->reader = sysprof_capture_reader_ref (self->reader); - st->pc = point_cache_new (); - st->max_n_addrs = 0; - st->begin_time = sysprof_capture_reader_get_start_time (self->reader); - st->end_time = sysprof_capture_reader_get_end_time (self->reader); - st->duration = st->end_time - st->begin_time; - - point_cache_add_set (st->pc, 1); - - task = g_task_new (self, NULL, apply_point_cache_cb, NULL); - g_task_set_source_tag (task, sysprof_depth_visualizer_row_reload); - g_task_set_task_data (task, st, (GDestroyNotify) state_free); - g_task_run_in_thread (task, sysprof_depth_visualizer_row_worker); -} - -static void -sysprof_depth_visualizer_row_set_reader (SysprofVisualizerRow *row, - SysprofCaptureReader *reader) -{ - SysprofDepthVisualizerRow *self = (SysprofDepthVisualizerRow *)row; - - g_assert (SYSPROF_IS_DEPTH_VISUALIZER_ROW (self)); - - if (self->reader != reader) - { - if (self->reader != NULL) - { - sysprof_capture_reader_unref (self->reader); - self->reader = NULL; - } - - if (reader != NULL) - { - self->reader = sysprof_capture_reader_ref (reader); - sysprof_depth_visualizer_row_reload (self); - } - } -} - -static gboolean -sysprof_depth_visualizer_row_draw (GtkWidget *widget, - cairo_t *cr) -{ - SysprofDepthVisualizerRow *self = (SysprofDepthVisualizerRow *)widget; - GtkStyleContext *style_context; - GtkAllocation alloc; - const Point *points; - guint n_points = 0; - GdkRGBA user; - GdkRGBA system; - - g_assert (SYSPROF_IS_DEPTH_VISUALIZER_ROW (self)); - g_assert (cr != NULL); - - GTK_WIDGET_CLASS (sysprof_depth_visualizer_row_parent_class)->draw (widget, cr); - - if (self->points == NULL) - return GDK_EVENT_PROPAGATE; - - style_context = gtk_widget_get_style_context (widget); - gtk_style_context_get_color (style_context, - gtk_style_context_get_state (style_context), - &user); - gdk_rgba_parse (&system, "#ef2929"); - - gtk_widget_get_allocation (widget, &alloc); - - if ((points = point_cache_get_points (self->points, 1, &n_points))) - { - g_autofree SysprofVisualizerRowAbsolutePoint *out_points = NULL; - gint last = 1; - - out_points = g_new (SysprofVisualizerRowAbsolutePoint, n_points); - sysprof_visualizer_row_translate_points (SYSPROF_VISUALIZER_ROW (widget), - (const SysprofVisualizerRowRelativePoint *)points, - n_points, out_points, n_points); - - cairo_set_line_width (cr, 1.0); - gdk_cairo_set_source_rgba (cr, &user); - - if (n_points > 0 && points[0].y < 0) - gdk_cairo_set_source_rgba (cr, &system); - - for (guint i = 0; i < n_points; i++) - { - if ((points[i].y < 0 && last > 0) || - (points[i].y > 0 && last < 0)) - cairo_stroke (cr); - - last = points[i].y > 0 ? 1 : -1; - - cairo_move_to (cr, .5 + alloc.x + (guint)out_points[i].x, alloc.height); - cairo_line_to (cr, .5 + alloc.x + out_points[i].x, out_points[i].y); - - if (last > 0) - gdk_cairo_set_source_rgba (cr, &user); - else - gdk_cairo_set_source_rgba (cr, &system); - } - - cairo_stroke (cr); - } - - return GDK_EVENT_PROPAGATE; -} - -static gboolean -sysprof_depth_visualizer_row_do_reload (gpointer data) -{ - SysprofDepthVisualizerRow *self = data; - self->reload_source = 0; - sysprof_depth_visualizer_row_reload (self); - return G_SOURCE_REMOVE; -} - -static void -sysprof_depth_visualizer_row_queue_reload (SysprofDepthVisualizerRow *self) -{ - g_assert (SYSPROF_IS_DEPTH_VISUALIZER_ROW (self)); - - if (self->reload_source) - g_source_remove (self->reload_source); - - self->reload_source = gdk_threads_add_idle (sysprof_depth_visualizer_row_do_reload, self); -} - -static void -sysprof_depth_visualizer_row_size_allocate (GtkWidget *widget, - GtkAllocation *alloc) -{ - GTK_WIDGET_CLASS (sysprof_depth_visualizer_row_parent_class)->size_allocate (widget, alloc); - - sysprof_depth_visualizer_row_queue_reload (SYSPROF_DEPTH_VISUALIZER_ROW (widget)); -} - -static void -sysprof_depth_visualizer_row_finalize (GObject *object) -{ - SysprofDepthVisualizerRow *self = (SysprofDepthVisualizerRow *)object; - - g_clear_pointer (&self->reader, sysprof_capture_reader_unref); - - if (self->reload_source) - { - g_source_remove (self->reload_source); - self->reload_source = 0; - } - - G_OBJECT_CLASS (sysprof_depth_visualizer_row_parent_class)->finalize (object); -} - -static void -sysprof_depth_visualizer_row_class_init (SysprofDepthVisualizerRowClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - SysprofVisualizerRowClass *row_class = SYSPROF_VISUALIZER_ROW_CLASS (klass); - - object_class->finalize = sysprof_depth_visualizer_row_finalize; - - widget_class->draw = sysprof_depth_visualizer_row_draw; - widget_class->size_allocate = sysprof_depth_visualizer_row_size_allocate; - - row_class->set_reader = sysprof_depth_visualizer_row_set_reader; -} - -static void -sysprof_depth_visualizer_row_init (SysprofDepthVisualizerRow *self) -{ - PangoAttrList *attrs = pango_attr_list_new (); - GtkWidget *label; - - pango_attr_list_insert (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL * PANGO_SCALE_SMALL)); - label = g_object_new (GTK_TYPE_LABEL, - "label", _("Stack Depth"), - "attributes", attrs, - "visible", TRUE, - "xalign", 0.0f, - "yalign", 0.0f, - NULL); - gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (label)); - pango_attr_list_unref (attrs); -} diff --git a/src/libsysprof-ui/sysprof-depth-visualizer.c b/src/libsysprof-ui/sysprof-depth-visualizer.c new file mode 100644 index 00000000..08183eb5 --- /dev/null +++ b/src/libsysprof-ui/sysprof-depth-visualizer.c @@ -0,0 +1,430 @@ +/* sysprof-depth-visualizer.c + * + * Copyright 2019 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#define G_LOG_DOMAIN "sysprof-depth-visualizer" + +#include "config.h" + +#include + +#include "pointcache.h" +#include "sysprof-depth-visualizer.h" + +struct _SysprofDepthVisualizer +{ + SysprofVisualizer parent_instance; + SysprofCaptureReader *reader; + PointCache *points; + guint reload_source; + guint mode; + GtkAllocation last_alloc; +}; + +typedef struct +{ + SysprofCaptureReader *reader; + PointCache *pc; + gint64 begin_time; + gint64 end_time; + gint64 duration; + guint max_n_addrs; + guint mode; +} State; + +G_DEFINE_TYPE (SysprofDepthVisualizer, sysprof_depth_visualizer, SYSPROF_TYPE_VISUALIZER) + +static void +state_free (State *st) +{ + g_clear_pointer (&st->reader, sysprof_capture_reader_unref); + g_clear_pointer (&st->pc, point_cache_unref); + g_slice_free (State, st); +} + +static gboolean +discover_max_n_addr (const SysprofCaptureFrame *frame, + gpointer user_data) +{ + const SysprofCaptureSample *sample = (const SysprofCaptureSample *)frame; + State *st = user_data; + + g_assert (frame != NULL); + g_assert (frame->type == SYSPROF_CAPTURE_FRAME_SAMPLE); + g_assert (st != NULL); + + st->max_n_addrs = MAX (st->max_n_addrs, sample->n_addrs); + + return TRUE; +} + +static gboolean +build_point_cache_cb (const SysprofCaptureFrame *frame, + gpointer user_data) +{ + const SysprofCaptureSample *sample = (const SysprofCaptureSample *)frame; + State *st = user_data; + gdouble x, y; + gboolean has_kernel = FALSE; + + g_assert (frame != NULL); + g_assert (frame->type == SYSPROF_CAPTURE_FRAME_SAMPLE); + g_assert (st != NULL); + + x = (frame->time - st->begin_time) / (gdouble)st->duration; + y = sample->n_addrs / (gdouble)st->max_n_addrs; + + /* If this contains a context-switch (meaning we're going into the kernel + * to do some work, use a negative value for Y so that we know later on + * that we should draw it with a different color (after removing the negation + * on the value. + * + * We skip past the first index, which is always a context switch as it is + * our perf handler. + */ + for (guint i = 1; i < sample->n_addrs; i++) + { + SysprofAddressContext kind; + + if (sysprof_address_is_context_switch (sample->addrs[i], &kind)) + { + has_kernel = TRUE; + y = -y; + break; + } + } + + if (!has_kernel) + point_cache_add_point_to_set (st->pc, 1, x, y); + else + point_cache_add_point_to_set (st->pc, 2, x, y); + + return TRUE; +} + +static void +sysprof_depth_visualizer_worker (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + static const SysprofCaptureFrameType types[] = { SYSPROF_CAPTURE_FRAME_SAMPLE, }; + g_autoptr(SysprofCaptureCursor) cursor = NULL; + SysprofCaptureCondition *condition; + State *st = task_data; + + g_assert (G_IS_TASK (task)); + g_assert (SYSPROF_IS_DEPTH_VISUALIZER (source_object)); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + if (st->duration != 0) + { + cursor = sysprof_capture_cursor_new (st->reader); + condition = sysprof_capture_condition_new_where_type_in (G_N_ELEMENTS (types), types); + sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition)); + + sysprof_capture_cursor_foreach (cursor, discover_max_n_addr, st); + sysprof_capture_cursor_reset (cursor); + sysprof_capture_cursor_foreach (cursor, build_point_cache_cb, st); + } + + g_task_return_pointer (task, + g_steal_pointer (&st->pc), + (GDestroyNotify) point_cache_unref); +} + +static void +apply_point_cache_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + SysprofDepthVisualizer *self = (SysprofDepthVisualizer *)object; + PointCache *pc; + + g_assert (SYSPROF_IS_DEPTH_VISUALIZER (self)); + g_assert (G_IS_TASK (result)); + + if ((pc = g_task_propagate_pointer (G_TASK (result), NULL))) + { + g_clear_pointer (&self->points, point_cache_unref); + self->points = g_steal_pointer (&pc); + gtk_widget_queue_draw (GTK_WIDGET (self)); + } +} + +static void +sysprof_depth_visualizer_reload (SysprofDepthVisualizer *self) +{ + g_autoptr(GTask) task = NULL; + GtkAllocation alloc; + State *st; + + g_assert (SYSPROF_IS_DEPTH_VISUALIZER (self)); + + gtk_widget_get_allocation (GTK_WIDGET (self), &alloc); + + st = g_slice_new0 (State); + st->reader = sysprof_capture_reader_ref (self->reader); + st->pc = point_cache_new (); + st->max_n_addrs = 0; + st->begin_time = sysprof_capture_reader_get_start_time (self->reader); + st->end_time = sysprof_capture_reader_get_end_time (self->reader); + st->duration = st->end_time - st->begin_time; + st->mode = self->mode; + + point_cache_add_set (st->pc, 1); + point_cache_add_set (st->pc, 2); + + task = g_task_new (self, NULL, apply_point_cache_cb, NULL); + g_task_set_source_tag (task, sysprof_depth_visualizer_reload); + g_task_set_task_data (task, st, (GDestroyNotify) state_free); + g_task_run_in_thread (task, sysprof_depth_visualizer_worker); +} + +static void +sysprof_depth_visualizer_set_reader (SysprofVisualizer *row, + SysprofCaptureReader *reader) +{ + SysprofDepthVisualizer *self = (SysprofDepthVisualizer *)row; + + g_assert (SYSPROF_IS_DEPTH_VISUALIZER (self)); + + if (self->reader != reader) + { + if (self->reader != NULL) + { + sysprof_capture_reader_unref (self->reader); + self->reader = NULL; + } + + if (reader != NULL) + { + self->reader = sysprof_capture_reader_ref (reader); + sysprof_depth_visualizer_reload (self); + } + } +} + +static gboolean +sysprof_depth_visualizer_draw (GtkWidget *widget, + cairo_t *cr) +{ + SysprofDepthVisualizer *self = (SysprofDepthVisualizer *)widget; + GtkAllocation alloc; + GdkRectangle clip; + const Point *points; + gboolean ret; + guint n_points = 0; + GdkRGBA user; + GdkRGBA system; + + g_assert (SYSPROF_IS_DEPTH_VISUALIZER (self)); + g_assert (cr != NULL); + + ret = GTK_WIDGET_CLASS (sysprof_depth_visualizer_parent_class)->draw (widget, cr); + + if (self->points == NULL) + return ret; + + gdk_rgba_parse (&user, "#1a5fb4"); + gdk_rgba_parse (&system, "#3584e4"); + + gtk_widget_get_allocation (widget, &alloc); + + if (!gdk_cairo_get_clip_rectangle (cr, &clip)) + return ret; + + /* Draw user-space stacks */ + if (self->mode != SYSPROF_DEPTH_VISUALIZER_KERNEL_ONLY && + (points = point_cache_get_points (self->points, 1, &n_points))) + { + g_autofree SysprofVisualizerAbsolutePoint *out_points = NULL; + + out_points = g_new (SysprofVisualizerAbsolutePoint, n_points); + sysprof_visualizer_translate_points (SYSPROF_VISUALIZER (widget), + (const SysprofVisualizerRelativePoint *)points, + n_points, out_points, n_points); + + cairo_set_line_width (cr, 1.0); + gdk_cairo_set_source_rgba (cr, &user); + + for (guint i = 0; i < n_points; i++) + { + gdouble x, y; + + x = out_points[i].x; + y = out_points[i].y; + + if (x < clip.x) + continue; + + if (x > clip.x + clip.width) + break; + + for (guint j = i + 1; j < n_points; j++) + { + if (out_points[j].x != x) + break; + + y = MIN (y, out_points[j].y); + } + + x += alloc.x; + + cairo_move_to (cr, (guint)x + .5, alloc.height); + cairo_line_to (cr, (guint)x + .5, y); + } + + cairo_stroke (cr); + } + + /* Draw kernel-space stacks */ + if (self->mode != SYSPROF_DEPTH_VISUALIZER_USER_ONLY && + (points = point_cache_get_points (self->points, 2, &n_points))) + { + g_autofree SysprofVisualizerAbsolutePoint *out_points = NULL; + + out_points = g_new (SysprofVisualizerAbsolutePoint, n_points); + sysprof_visualizer_translate_points (SYSPROF_VISUALIZER (widget), + (const SysprofVisualizerRelativePoint *)points, + n_points, out_points, n_points); + + cairo_set_line_width (cr, 1.0); + gdk_cairo_set_source_rgba (cr, &system); + + for (guint i = 0; i < n_points; i++) + { + gdouble x, y; + + x = out_points[i].x; + y = out_points[i].y; + + if (x < clip.x) + continue; + + if (x > clip.x + clip.width) + break; + + for (guint j = i + 1; j < n_points; j++) + { + if (out_points[j].x != x) + break; + + y = MIN (y, out_points[j].y); + } + + x += alloc.x; + + cairo_move_to (cr, (guint)x + .5, alloc.height); + cairo_line_to (cr, (guint)x + .5, y); + } + + cairo_stroke (cr); + } + + return ret; +} + +static gboolean +sysprof_depth_visualizer_do_reload (gpointer data) +{ + SysprofDepthVisualizer *self = data; + self->reload_source = 0; + sysprof_depth_visualizer_reload (self); + return G_SOURCE_REMOVE; +} + +static void +sysprof_depth_visualizer_queue_reload (SysprofDepthVisualizer *self) +{ + g_assert (SYSPROF_IS_DEPTH_VISUALIZER (self)); + + if (self->reload_source) + g_source_remove (self->reload_source); + + self->reload_source = gdk_threads_add_idle (sysprof_depth_visualizer_do_reload, self); +} + +static void +sysprof_depth_visualizer_size_allocate (GtkWidget *widget, + GtkAllocation *alloc) +{ + SysprofDepthVisualizer *self = (SysprofDepthVisualizer *)widget; + + GTK_WIDGET_CLASS (sysprof_depth_visualizer_parent_class)->size_allocate (widget, alloc); + + if (alloc->width != self->last_alloc.x || + alloc->height != self->last_alloc.height) + { + sysprof_depth_visualizer_queue_reload (SYSPROF_DEPTH_VISUALIZER (widget)); + self->last_alloc = *alloc; + } +} + +static void +sysprof_depth_visualizer_finalize (GObject *object) +{ + SysprofDepthVisualizer *self = (SysprofDepthVisualizer *)object; + + g_clear_pointer (&self->reader, sysprof_capture_reader_unref); + + if (self->reload_source) + { + g_source_remove (self->reload_source); + self->reload_source = 0; + } + + G_OBJECT_CLASS (sysprof_depth_visualizer_parent_class)->finalize (object); +} + +static void +sysprof_depth_visualizer_class_init (SysprofDepthVisualizerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + SysprofVisualizerClass *row_class = SYSPROF_VISUALIZER_CLASS (klass); + + object_class->finalize = sysprof_depth_visualizer_finalize; + + widget_class->draw = sysprof_depth_visualizer_draw; + widget_class->size_allocate = sysprof_depth_visualizer_size_allocate; + + row_class->set_reader = sysprof_depth_visualizer_set_reader; +} + +static void +sysprof_depth_visualizer_init (SysprofDepthVisualizer *self) +{ +} + +SysprofVisualizer * +sysprof_depth_visualizer_new (SysprofDepthVisualizerMode mode) +{ + SysprofDepthVisualizer *self; + + g_return_val_if_fail (mode == SYSPROF_DEPTH_VISUALIZER_COMBINED || + mode == SYSPROF_DEPTH_VISUALIZER_KERNEL_ONLY || + mode == SYSPROF_DEPTH_VISUALIZER_USER_ONLY, + NULL); + + self = g_object_new (SYSPROF_TYPE_DEPTH_VISUALIZER, NULL); + self->mode = mode; + + return SYSPROF_VISUALIZER (g_steal_pointer (&self)); +} diff --git a/src/libsysprof-ui/sysprof-depth-visualizer.h b/src/libsysprof-ui/sysprof-depth-visualizer.h new file mode 100644 index 00000000..425c4d42 --- /dev/null +++ b/src/libsysprof-ui/sysprof-depth-visualizer.h @@ -0,0 +1,40 @@ +/* sysprof-depth-visualizer.h + * + * Copyright 2019 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-visualizer.h" + +G_BEGIN_DECLS + +typedef enum +{ + SYSPROF_DEPTH_VISUALIZER_COMBINED, + SYSPROF_DEPTH_VISUALIZER_KERNEL_ONLY, + SYSPROF_DEPTH_VISUALIZER_USER_ONLY, +} SysprofDepthVisualizerMode; + +#define SYSPROF_TYPE_DEPTH_VISUALIZER (sysprof_depth_visualizer_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofDepthVisualizer, sysprof_depth_visualizer, SYSPROF, DEPTH_VISUALIZER, SysprofVisualizer) + +SysprofVisualizer *sysprof_depth_visualizer_new (SysprofDepthVisualizerMode mode); + +G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-details-page.c b/src/libsysprof-ui/sysprof-details-page.c new file mode 100644 index 00000000..13170f4c --- /dev/null +++ b/src/libsysprof-ui/sysprof-details-page.c @@ -0,0 +1,324 @@ +/* sysprof-details-page.c + * + * Copyright 2019 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#define G_LOG_DOMAIN "sysprof-details-page" + +#include "config.h" + +#include +#include +#include + +#include "sysprof-details-page.h" +#include "sysprof-ui-private.h" + +#define NSEC_PER_SEC (G_USEC_PER_SEC * 1000L) + +struct _SysprofDetailsPage +{ + SysprofPage parent_instance; + + /* Template Objects */ + DzlThreeGrid *three_grid; + GtkListStore *marks_store; + GtkTreeView *marks_view; + GtkLabel *counters; + GtkLabel *duration; + GtkLabel *filename; + GtkLabel *forks; + GtkLabel *marks; + GtkLabel *processes; + GtkLabel *samples; + GtkLabel *start_time; + GtkLabel *cpu_label; + + guint next_row; +}; + +G_DEFINE_TYPE (SysprofDetailsPage, sysprof_details_page, GTK_TYPE_BIN) + +#if GLIB_CHECK_VERSION(2, 56, 0) +# define _g_date_time_new_from_iso8601 g_date_time_new_from_iso8601 +#else +static GDateTime * +_g_date_time_new_from_iso8601 (const gchar *str, + GTimeZone *default_tz) +{ + GTimeVal tv; + + if (g_time_val_from_iso8601 (str, &tv)) + { + g_autoptr(GDateTime) dt = g_date_time_new_from_timeval_utc (&tv); + + if (default_tz) + return g_date_time_to_timezone (dt, default_tz); + else + return g_steal_pointer (&dt); + } + + return NULL; +} +#endif + +static void +sysprof_details_page_class_init (SysprofDetailsPageClass *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-details-page.ui"); + gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, counters); + gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, cpu_label); + gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, duration); + gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, filename); + gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, forks); + gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, marks); + gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, marks_store); + gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, marks_view); + gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, processes); + gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, samples); + gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, start_time); + gtk_widget_class_bind_template_child (widget_class, SysprofDetailsPage, three_grid); + + g_type_ensure (DZL_TYPE_THREE_GRID); +} + +static void +sysprof_details_page_init (SysprofDetailsPage *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); + + gtk_tree_selection_set_mode (gtk_tree_view_get_selection (self->marks_view), + GTK_SELECTION_MULTIPLE); + + self->next_row = 8; +} + +GtkWidget * +sysprof_details_page_new (void) +{ + return g_object_new (SYSPROF_TYPE_DETAILS_PAGE, NULL); +} + +static void +update_cpu_info_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + g_autoptr(SysprofDetailsPage) self = user_data; + g_autofree gchar *str = NULL; + + g_assert (SYSPROF_IS_DETAILS_PAGE (self)); + g_assert (G_IS_TASK (result)); + + if ((str = g_task_propagate_pointer (G_TASK (result), NULL))) + gtk_label_set_label (self->cpu_label, str); +} + +static gboolean +cpu_info_cb (const SysprofCaptureFrame *frame, + gpointer user_data) +{ + const SysprofCaptureFileChunk *fc = (gpointer)frame; + const gchar *endptr; + const gchar *line; + gchar **str = user_data; + + endptr = (gchar *)fc->data + fc->len; + line = memmem ((gchar *)fc->data, fc->len, "model name", 10); + endptr = memchr (line, '\n', endptr - line); + + if (endptr) + { + gchar *tmp = *str = g_strndup (line, endptr - line); + for (; *tmp && *tmp != ':'; tmp++) + *tmp = ' '; + if (*tmp == ':') + *tmp = ' '; + g_strstrip (*str); + return FALSE; + } + + return TRUE; +} + +static void +sysprof_details_page_update_cpu_info_worker (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + SysprofCaptureCursor *cursor = task_data; + gchar *str = NULL; + + g_assert (G_IS_TASK (task)); + g_assert (cursor != NULL); + + sysprof_capture_cursor_foreach (cursor, cpu_info_cb, &str); + g_task_return_pointer (task, g_steal_pointer (&str), g_free); +} + +static void +sysprof_details_page_update_cpu_info (SysprofDetailsPage *self, + SysprofCaptureReader *reader) +{ + g_autoptr(SysprofCaptureCursor) cursor = NULL; + g_autoptr(GTask) task = NULL; + + g_assert (SYSPROF_IS_DETAILS_PAGE (self)); + g_assert (reader != NULL); + + cursor = sysprof_capture_cursor_new (reader); + sysprof_capture_cursor_add_condition (cursor, + sysprof_capture_condition_new_where_file ("/proc/cpuinfo")); + + task = g_task_new (NULL, NULL, update_cpu_info_cb, g_object_ref (self)); + g_task_set_task_data (task, + g_steal_pointer (&cursor), + (GDestroyNotify) sysprof_capture_cursor_unref); + g_task_run_in_thread (task, sysprof_details_page_update_cpu_info_worker); +} + +void +sysprof_details_page_set_reader (SysprofDetailsPage *self, + SysprofCaptureReader *reader) +{ + g_autoptr(GDateTime) dt = NULL; + g_autoptr(GDateTime) local = NULL; + g_autofree gchar *duration_str = NULL; + const gchar *filename; + const gchar *capture_at; + SysprofCaptureStat st_buf; + gint64 duration; + + g_return_if_fail (SYSPROF_IS_DETAILS_PAGE (self)); + g_return_if_fail (reader != NULL); + + sysprof_details_page_update_cpu_info (self, reader); + + if (!(filename = sysprof_capture_reader_get_filename (reader))) + filename = _("Memory Capture"); + + gtk_label_set_label (self->filename, filename); + + if ((capture_at = sysprof_capture_reader_get_time (reader)) && + (dt = _g_date_time_new_from_iso8601 (capture_at, NULL)) && + (local = g_date_time_to_local (dt))) + { + g_autofree gchar *str = g_date_time_format (local, "%x %X"); + gtk_label_set_label (self->start_time, str); + } + + duration = sysprof_capture_reader_get_end_time (reader) - + sysprof_capture_reader_get_start_time (reader); + duration_str = g_strdup_printf (_("%0.4lf seconds"), duration / (gdouble)NSEC_PER_SEC); + gtk_label_set_label (self->duration, duration_str); + + if (sysprof_capture_reader_get_stat (reader, &st_buf)) + { +#define SET_FRAME_COUNT(field, TYPE) \ + G_STMT_START { \ + g_autofree gchar *str = NULL; \ + str = g_strdup_printf ("%"G_GSIZE_FORMAT, st_buf.frame_count[TYPE]); \ + gtk_label_set_label (self->field, str); \ + } G_STMT_END + + SET_FRAME_COUNT (samples, SYSPROF_CAPTURE_FRAME_SAMPLE); + SET_FRAME_COUNT (marks, SYSPROF_CAPTURE_FRAME_MARK); + SET_FRAME_COUNT (processes, SYSPROF_CAPTURE_FRAME_PROCESS); + SET_FRAME_COUNT (forks, SYSPROF_CAPTURE_FRAME_FORK); + SET_FRAME_COUNT (counters, SYSPROF_CAPTURE_FRAME_CTRSET); + +#undef SET_FRAME_COUNT + } +} + +void +sysprof_details_page_add_item (SysprofDetailsPage *self, + GtkWidget *left, + GtkWidget *center) +{ + g_return_if_fail (SYSPROF_IS_DETAILS_PAGE (self)); + g_return_if_fail (!left || GTK_IS_WIDGET (left)); + g_return_if_fail (!center || GTK_IS_WIDGET (center)); + + if (left) + gtk_container_add_with_properties (GTK_CONTAINER (self->three_grid), left, + "row", self->next_row, + "column", DZL_THREE_GRID_COLUMN_LEFT, + NULL); + + if (center) + gtk_container_add_with_properties (GTK_CONTAINER (self->three_grid), center, + "row", self->next_row, + "column", DZL_THREE_GRID_COLUMN_CENTER, + NULL); + + self->next_row++; +} + +void +sysprof_details_page_add_mark (SysprofDetailsPage *self, + const gchar *mark, + gint64 min, + gint64 max, + gint64 avg, + gint64 hits) +{ + GtkTreeIter iter; + + g_return_if_fail (SYSPROF_IS_DETAILS_PAGE (self)); + + gtk_list_store_append (self->marks_store, &iter); + gtk_list_store_set (self->marks_store, &iter, + 0, mark, + 1, min ? _sysprof_format_duration (min) : "—", + 2, max ? _sysprof_format_duration (max) : "—", + 3, avg ? _sysprof_format_duration (avg) : "—", + 4, hits, + -1); +} + +void +sysprof_details_page_add_marks (SysprofDetailsPage *self, + const SysprofMarkStat *marks, + guint n_marks) +{ + g_return_if_fail (SYSPROF_IS_DETAILS_PAGE (self)); + g_return_if_fail (marks != NULL || n_marks == 0); + + if (marks == NULL || n_marks == 0) + return; + + /* Be reasonable */ + if (n_marks > 100) + n_marks = 100; + + for (guint i = 0; i < n_marks; i++) + sysprof_details_page_add_mark (self, + marks[i].name, + marks[i].min, + marks[i].max, + marks[i].avg, + marks[i].count); +} diff --git a/src/libsysprof-ui/sysprof-details-page.h b/src/libsysprof-ui/sysprof-details-page.h new file mode 100644 index 00000000..251bbe56 --- /dev/null +++ b/src/libsysprof-ui/sysprof-details-page.h @@ -0,0 +1,60 @@ +/* sysprof-details-page.h + * + * Copyright 2019 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include +#include + +G_BEGIN_DECLS + +SYSPROF_ALIGNED_BEGIN (8) +typedef struct +{ + gchar name[152]; + guint64 count; + gint64 max; + gint64 min; + gint64 avg; + guint64 avg_count; +} SysprofMarkStat +SYSPROF_ALIGNED_END (8); + +#define SYSPROF_TYPE_DETAILS_PAGE (sysprof_details_page_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofDetailsPage, sysprof_details_page, SYSPROF, DETAILS_PAGE, GtkBin) + +GtkWidget *sysprof_details_page_new (void); +void sysprof_details_page_set_reader (SysprofDetailsPage *self, + SysprofCaptureReader *reader); +void sysprof_details_page_add_marks (SysprofDetailsPage *self, + const SysprofMarkStat *marks, + guint n_marks); +void sysprof_details_page_add_mark (SysprofDetailsPage *self, + const gchar *mark, + gint64 min, + gint64 max, + gint64 avg, + gint64 hits); +void sysprof_details_page_add_item (SysprofDetailsPage *self, + GtkWidget *left, + GtkWidget *center); + +G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-details-page.ui b/src/libsysprof-ui/sysprof-details-page.ui new file mode 100644 index 00000000..c7373a39 --- /dev/null +++ b/src/libsysprof-ui/sysprof-details-page.ui @@ -0,0 +1,372 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/libsysprof-ui/sysprof-display.c b/src/libsysprof-ui/sysprof-display.c index 7f31a546..d9dbf24f 100644 --- a/src/libsysprof-ui/sysprof-display.c +++ b/src/libsysprof-ui/sysprof-display.c @@ -22,31 +22,49 @@ #include "config.h" +#include #include -#include -#include -#include - -#include "sysprof-profiler-assistant.h" -#include "sysprof-capture-view.h" +#include "sysprof-details-page.h" #include "sysprof-display.h" -#include "sysprof-empty-state-view.h" +#include "sysprof-profiler-assistant.h" +#include "sysprof-failed-state-view.h" #include "sysprof-recording-state-view.h" +#include "sysprof-theme-manager.h" #include "sysprof-ui-private.h" +#include "sysprof-visualizers-frame.h" +#include "sysprof-visualizer-group-private.h" + +#include "sysprof-battery-aid.h" +#include "sysprof-callgraph-aid.h" +#include "sysprof-counters-aid.h" +#include "sysprof-cpu-aid.h" +#include "sysprof-logs-aid.h" +#include "sysprof-marks-aid.h" + +typedef enum +{ + SYSPROF_CAPTURE_FLAGS_CAN_REPLAY = 1 << 1, +} SysprofCaptureFlags; typedef struct { + SysprofCaptureReader *reader; + SysprofCaptureCondition *filter; GFile *file; SysprofProfiler *profiler; GError *error; - /* Template Objects */ - SysprofProfilerAssistant *assistant; - SysprofCaptureView *capture_view; - SysprofEmptyStateView *failed_view; - SysprofRecordingStateView *recording_view; + /* Template Widgets */ + SysprofVisualizersFrame *visualizers; + GtkStack *pages; + SysprofDetailsPage *details; GtkStack *stack; + SysprofProfilerAssistant *assistant; + SysprofRecordingStateView *recording_view; + SysprofFailedStateView *failed_view; + + SysprofCaptureFlags flags; } SysprofDisplayPrivate; G_DEFINE_TYPE_WITH_PRIVATE (SysprofDisplay, sysprof_display, GTK_TYPE_BIN) @@ -57,43 +75,29 @@ enum { PROP_CAN_SAVE, PROP_RECORDING, PROP_TITLE, + PROP_VISIBLE_PAGE, N_PROPS }; static GParamSpec *properties [N_PROPS]; -/** - * sysprof_display_new: - * - * Create a new #SysprofDisplay. - * - * Returns: (transfer full): a newly created #SysprofDisplay - * - * Since: 3.34 - */ -GtkWidget * -sysprof_display_new (void) -{ - return g_object_new (SYSPROF_TYPE_DISPLAY, NULL); -} - static void -sysprof_display_load_cb (SysprofCaptureView *view, - GAsyncResult *result, - gpointer user_data) +update_title_child_property (SysprofDisplay *self) { - g_autoptr(SysprofDisplay) self = user_data; - g_autoptr(GError) error = NULL; + GtkWidget *parent; - g_assert (SYSPROF_IS_CAPTURE_VIEW (view)); - g_assert (G_IS_ASYNC_RESULT (result)); g_assert (SYSPROF_IS_DISPLAY (self)); - if (!sysprof_capture_view_load_finish (view, result, &error)) - g_warning ("Failed to load capture: %s", error->message); + if ((parent = gtk_widget_get_parent (GTK_WIDGET (self))) && GTK_IS_NOTEBOOK (parent)) + { + g_autofree gchar *title = sysprof_display_dup_title (self); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_REPLAY]); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_CAN_SAVE]); + gtk_container_child_set (GTK_CONTAINER (parent), GTK_WIDGET (self), + "menu-label", title, + NULL); + } + + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]); } static void @@ -141,12 +145,10 @@ sysprof_display_profiler_stopped_cb (SysprofDisplay *self, goto notify; } - sysprof_capture_view_load_async (priv->capture_view, - reader, - NULL, - (GAsyncReadyCallback) sysprof_display_load_cb, - g_object_ref (self)); - gtk_stack_set_visible_child (priv->stack, GTK_WIDGET (priv->capture_view)); + sysprof_display_load_async (self, + reader, + NULL, NULL, NULL); + gtk_stack_set_visible_child_name (priv->stack, "view"); } notify: @@ -201,11 +203,20 @@ sysprof_display_start_recording_cb (SysprofDisplay *self, sysprof_profiler_start (profiler); } +static gboolean +sysprof_display_get_is_recording (SysprofDisplay *self) +{ + SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); + + g_assert (SYSPROF_IS_DISPLAY (self)); + + return GTK_WIDGET (priv->recording_view) == gtk_stack_get_visible_child (priv->stack); +} + gchar * sysprof_display_dup_title (SysprofDisplay *self) { SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - SysprofCaptureReader *reader; g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), NULL); @@ -221,44 +232,100 @@ sysprof_display_dup_title (SysprofDisplay *self) if (priv->file != NULL) return g_file_get_basename (priv->file); - if ((reader = sysprof_capture_view_get_reader (priv->capture_view))) + if (priv->reader != NULL) { const gchar *filename; - if ((filename = sysprof_capture_reader_get_filename (reader))) + if ((filename = sysprof_capture_reader_get_filename (priv->reader))) return g_path_get_basename (filename); } return g_strdup (_("New Recording")); } -static void -update_title_child_property (SysprofDisplay *self) +/** + * sysprof_display_new: + * + * Create a new #SysprofDisplay. + * + * Returns: (transfer full): a newly created #SysprofDisplay + */ +GtkWidget * +sysprof_display_new (void) { - GtkWidget *parent; - - g_assert (SYSPROF_IS_DISPLAY (self)); - - if ((parent = gtk_widget_get_parent (GTK_WIDGET (self))) && GTK_IS_NOTEBOOK (parent)) - { - g_autofree gchar *title = sysprof_display_dup_title (self); - - gtk_container_child_set (GTK_CONTAINER (parent), GTK_WIDGET (self), - "menu-label", title, - NULL); - } - - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]); + return g_object_new (SYSPROF_TYPE_DISPLAY, NULL); } -static gboolean -sysprof_display_get_is_recording (SysprofDisplay *self) +static void +sysprof_display_notify_selection_cb (SysprofDisplay *self, + GParamSpec *pspec, + SysprofVisualizersFrame *visualizers) { SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); + SysprofSelection *selection; g_assert (SYSPROF_IS_DISPLAY (self)); + g_assert (SYSPROF_IS_VISUALIZERS_FRAME (visualizers)); - return GTK_WIDGET (priv->recording_view) == gtk_stack_get_visible_child (priv->stack); + g_clear_pointer (&priv->filter, sysprof_capture_condition_unref); + + if ((selection = sysprof_visualizers_frame_get_selection (visualizers))) + { + SysprofCaptureCondition *cond = NULL; + guint n_ranges = sysprof_selection_get_n_ranges (selection); + + for (guint i = 0; i < n_ranges; i++) + { + SysprofCaptureCondition *c; + gint64 begin, end; + + sysprof_selection_get_nth_range (selection, i, &begin, &end); + c = sysprof_capture_condition_new_where_time_between (begin, end); + + if (cond == NULL) + cond = c; + else + cond = sysprof_capture_condition_new_or (cond, c); + } + + priv->filter = cond; + + /* Opportunistically load pages */ + if (priv->reader != NULL) + { + GList *pages = gtk_container_get_children (GTK_CONTAINER (priv->pages)); + + for (const GList *iter = pages; iter; iter = iter->next) + { + if (SYSPROF_IS_PAGE (iter->data)) + sysprof_page_load_async (iter->data, + priv->reader, + selection, + priv->filter, + NULL, NULL, NULL); + } + + g_list_free (pages); + } + } +} + +static void +change_page_cb (GSimpleAction *action, + GVariant *param, + gpointer user_data) +{ + SysprofDisplay *self = user_data; + SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); + + g_assert (G_IS_SIMPLE_ACTION (action)); + g_assert (param != NULL); + + if (g_variant_is_of_type (param, G_VARIANT_TYPE_STRING)) + { + const gchar *str = g_variant_get_string (param, NULL); + gtk_stack_set_visible_child_name (priv->pages, str); + } } static void @@ -274,27 +341,14 @@ stop_recording_cb (GSimpleAction *action, sysprof_display_stop_recording (self); } -static void -sysprof_display_parent_set (GtkWidget *widget, - GtkWidget *old_parent) -{ - g_assert (SYSPROF_IS_DISPLAY (widget)); - - if (GTK_WIDGET_CLASS (sysprof_display_parent_class)->parent_set) - GTK_WIDGET_CLASS (sysprof_display_parent_class)->parent_set (widget, old_parent); - - update_title_child_property (SYSPROF_DISPLAY (widget)); -} - static void sysprof_display_finalize (GObject *object) { SysprofDisplay *self = (SysprofDisplay *)object; SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - g_clear_error (&priv->error); - g_clear_object (&priv->profiler); - g_clear_object (&priv->file); + g_clear_pointer (&priv->reader, sysprof_capture_reader_unref); + g_clear_pointer (&priv->filter, sysprof_capture_condition_unref); G_OBJECT_CLASS (sysprof_display_parent_class)->finalize (object); } @@ -305,7 +359,7 @@ sysprof_display_get_property (GObject *object, GValue *value, GParamSpec *pspec) { - SysprofDisplay *self = (SysprofDisplay *)object; + SysprofDisplay *self = SYSPROF_DISPLAY (object); switch (prop_id) { @@ -325,6 +379,29 @@ sysprof_display_get_property (GObject *object, g_value_take_string (value, sysprof_display_dup_title (self)); break; + case PROP_VISIBLE_PAGE: + g_value_set_object (value, sysprof_display_get_visible_page (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_display_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofDisplay *self = SYSPROF_DISPLAY (object); + + switch (prop_id) + { + case PROP_VISIBLE_PAGE: + sysprof_display_set_visible_page (self, g_value_get_object (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -338,8 +415,22 @@ sysprof_display_class_init (SysprofDisplayClass *klass) object_class->finalize = sysprof_display_finalize; object_class->get_property = sysprof_display_get_property; + object_class->set_property = sysprof_display_set_property; - widget_class->parent_set = sysprof_display_parent_set; + sysprof_theme_manager_register_resource (sysprof_theme_manager_get_default (), + NULL, + NULL, + "/org/gnome/sysprof/css/SysprofDisplay-shared.css"); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-display.ui"); + gtk_widget_class_set_css_name (widget_class, "SysprofDisplay"); + gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, assistant); + gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, details); + gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, failed_view); + gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, pages); + gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, recording_view); + gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, stack); + gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, visualizers); properties [PROP_CAN_REPLAY] = g_param_spec_boolean ("can-replay", @@ -367,20 +458,23 @@ sysprof_display_class_init (SysprofDisplayClass *klass) "Title", "The title of the display", NULL, - (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + (G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + properties [PROP_VISIBLE_PAGE] = + g_param_spec_object ("visible-page", + "Visible Page", + "Visible Page", + SYSPROF_TYPE_PAGE, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); g_object_class_install_properties (object_class, N_PROPS, properties); - gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-display.ui"); - gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, capture_view); - gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, failed_view); - gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, recording_view); - gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, assistant); - gtk_widget_class_bind_template_child_private (widget_class, SysprofDisplay, stack); - - g_type_ensure (SYSPROF_TYPE_CAPTURE_VIEW); - g_type_ensure (SYSPROF_TYPE_EMPTY_STATE_VIEW); + g_type_ensure (DZL_TYPE_MULTI_PANED); + g_type_ensure (SYSPROF_TYPE_DETAILS_PAGE); + g_type_ensure (SYSPROF_TYPE_FAILED_STATE_VIEW); + g_type_ensure (SYSPROF_TYPE_PROFILER_ASSISTANT); g_type_ensure (SYSPROF_TYPE_RECORDING_STATE_VIEW); + g_type_ensure (SYSPROF_TYPE_VISUALIZERS_FRAME); } static void @@ -388,42 +482,512 @@ sysprof_display_init (SysprofDisplay *self) { SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); g_autoptr(GSimpleActionGroup) group = g_simple_action_group_new (); - static const GActionEntry actions[] = { + static GActionEntry entries[] = { + { "page", change_page_cb, "s" }, { "stop-recording", stop_recording_cb }, }; + g_autoptr(GPropertyAction) page = NULL; gtk_widget_init_template (GTK_WIDGET (self)); - g_action_map_add_action_entries (G_ACTION_MAP (group), - actions, - G_N_ELEMENTS (actions), - self); - gtk_widget_insert_action_group (GTK_WIDGET (self), "display", G_ACTION_GROUP (group)); - g_signal_connect_object (priv->assistant, "start-recording", G_CALLBACK (sysprof_display_start_recording_cb), self, G_CONNECT_SWAPPED); + + g_signal_connect_object (priv->visualizers, + "notify::selection", + G_CALLBACK (sysprof_display_notify_selection_cb), + self, + G_CONNECT_SWAPPED); + + page = g_property_action_new ("page", priv->pages, "visible-child-name"); + g_action_map_add_action_entries (G_ACTION_MAP (group), + entries, + G_N_ELEMENTS (entries), + self); + gtk_widget_insert_action_group (GTK_WIDGET (self), "display", G_ACTION_GROUP (group)); } -/** - * sysprof_display_get_profiler: - * - * Gets the proflier for the display. - * - * Returns: (transfer none) (nullable): a #SysprofProfiler or %NULL - * - * Since: 3.34 - */ -SysprofProfiler * -sysprof_display_get_profiler (SysprofDisplay *self) +void +sysprof_display_add_group (SysprofDisplay *self, + SysprofVisualizerGroup *group) +{ + SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); + + g_return_if_fail (SYSPROF_IS_DISPLAY (self)); + g_return_if_fail (SYSPROF_IS_VISUALIZER_GROUP (group)); + + if (priv->reader != NULL) + _sysprof_visualizer_group_set_reader (group, priv->reader); + + gtk_container_add (GTK_CONTAINER (priv->visualizers), GTK_WIDGET (group)); +} + +void +sysprof_display_add_page (SysprofDisplay *self, + SysprofPage *page) +{ + SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); + SysprofSelection *selection; + const gchar *title; + + g_return_if_fail (SYSPROF_IS_DISPLAY (self)); + g_return_if_fail (SYSPROF_IS_PAGE (page)); + + title = sysprof_page_get_title (page); + + gtk_container_add_with_properties (GTK_CONTAINER (priv->pages), GTK_WIDGET (page), + "title", title, + NULL); + + selection = sysprof_visualizers_frame_get_selection (priv->visualizers); + + sysprof_page_set_size_group (page, + sysprof_visualizers_frame_get_size_group (priv->visualizers)); + + sysprof_page_set_hadjustment (page, + sysprof_visualizers_frame_get_hadjustment (priv->visualizers)); + + if (priv->reader != NULL) + sysprof_page_load_async (page, + priv->reader, + selection, + priv->filter, + NULL, NULL, NULL); +} + +SysprofPage * +sysprof_display_get_visible_page (SysprofDisplay *self) +{ + SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); + GtkWidget *visible_page; + + g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), NULL); + + visible_page = gtk_stack_get_visible_child (priv->pages); + + if (SYSPROF_IS_PAGE (visible_page)) + return SYSPROF_PAGE (visible_page); + + return NULL; +} + +void +sysprof_display_set_visible_page (SysprofDisplay *self, + SysprofPage *page) +{ + SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); + + g_return_if_fail (SYSPROF_IS_DISPLAY (self)); + g_return_if_fail (SYSPROF_IS_PAGE (page)); + + gtk_stack_set_visible_child (priv->pages, GTK_WIDGET (page)); +} + +static void +sysprof_display_present_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + SysprofAid *aid = (SysprofAid *)object; + g_autoptr(GTask) task = user_data; + g_autoptr(GError) error = NULL; + guint *n_active; + + g_assert (SYSPROF_IS_AID (aid)); + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (G_IS_TASK (task)); + + if (!sysprof_aid_present_finish (aid, result, &error)) + g_warning ("Failed to present aid %s: %s", G_OBJECT_TYPE_NAME (aid), error->message); + + n_active = g_task_get_task_data (task); + + (*n_active)--; + + if (n_active == 0) + g_task_return_boolean (task, TRUE); +} + +static void +sysprof_display_present_async (SysprofDisplay *self, + SysprofCaptureReader *reader, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(GPtrArray) aids = NULL; + g_autoptr(GTask) task = NULL; + + g_return_if_fail (SYSPROF_IS_DISPLAY (self)); + g_return_if_fail (reader != NULL); + g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + + aids = g_ptr_array_new_with_free_func (g_object_unref); + g_ptr_array_add (aids, sysprof_battery_aid_new ()); + g_ptr_array_add (aids, sysprof_counters_aid_new ()); + g_ptr_array_add (aids, sysprof_cpu_aid_new ()); + g_ptr_array_add (aids, sysprof_callgraph_aid_new ()); + g_ptr_array_add (aids, sysprof_logs_aid_new ()); + g_ptr_array_add (aids, sysprof_marks_aid_new ()); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, sysprof_display_present_async); + + if (aids->len == 0) + { + g_task_return_boolean (task, TRUE); + return; + } + + g_task_set_task_data (task, g_memdup (&aids->len, sizeof aids->len), g_free); + + for (guint i = 0; i < aids->len; i++) + { + SysprofAid *aid = g_ptr_array_index (aids, i); + + sysprof_aid_present_async (aid, + reader, + self, + cancellable, + sysprof_display_present_cb, + g_object_ref (task)); + } +} + +static gboolean +sysprof_display_present_finish (SysprofDisplay *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), FALSE); + g_return_val_if_fail (G_IS_TASK (result), FALSE); + + return g_task_propagate_boolean (G_TASK (result), error); +} + +static void +sysprof_display_scan_worker (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + SysprofDisplay *self = source_object; + SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); + SysprofCaptureReader *reader = task_data; + g_autoptr(GHashTable) mark_stats = NULL; + g_autoptr(GArray) marks = NULL; + SysprofCaptureFrame frame; + SysprofCaptureStat st = {{0}}; + SysprofCaptureFlags flags = 0; + + g_assert (G_IS_TASK (task)); + g_assert (SYSPROF_IS_DISPLAY (self)); + g_assert (reader != NULL); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + /* Scan the reader until the end so that we know we have gotten + * all of the timing data loaded into the underlying reader. + */ + + mark_stats = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + marks = g_array_new (FALSE, FALSE, sizeof (SysprofMarkStat)); + + while (sysprof_capture_reader_peek_frame (reader, &frame)) + { + st.frame_count[frame.type]++; + + if (frame.type == SYSPROF_CAPTURE_FRAME_METADATA) + { + const SysprofCaptureMetadata *meta; + + if ((meta = sysprof_capture_reader_read_metadata (reader))) + { + if (g_strcmp0 (meta->id, "local-profiler") == 0) + flags |= SYSPROF_CAPTURE_FLAGS_CAN_REPLAY; + } + } + else if (frame.type == SYSPROF_CAPTURE_FRAME_MARK) + { + const SysprofCaptureMark *mark; + + if ((mark = sysprof_capture_reader_read_mark (reader))) + { + SysprofMarkStat *mstat; + gchar name[152]; + gpointer idx; + + g_snprintf (name, sizeof name, "%s:%s", mark->group, mark->name); + + if (!(idx = g_hash_table_lookup (mark_stats, name))) + { + SysprofMarkStat empty = {{0}}; + + g_strlcpy (empty.name, name, sizeof empty.name); + g_array_append_val (marks, empty); + idx = GUINT_TO_POINTER (marks->len); + g_hash_table_insert (mark_stats, g_strdup (name), idx); + } + + mstat = &g_array_index (marks, SysprofMarkStat, GPOINTER_TO_UINT (idx) - 1); + + if (mark->duration > 0) + { + if (mstat->min == 0 || mark->duration < mstat->min) + mstat->min = mark->duration; + } + + if (mark->duration > mstat->max) + mstat->max = mark->duration; + + if (mark->duration > 0) + { + mstat->avg += mark->duration; + mstat->avg_count++; + } + + mstat->count++; + } + } + else + { + sysprof_capture_reader_skip (reader); + } + } + + { + GHashTableIter iter; + gpointer k,v; + + g_hash_table_iter_init (&iter, mark_stats); + while (g_hash_table_iter_next (&iter, &k, &v)) + { + guint idx = GPOINTER_TO_UINT (v) - 1; + SysprofMarkStat *mstat = &g_array_index (marks, SysprofMarkStat, idx); + + if (mstat->avg_count > 0 && mstat->avg > 0) + mstat->avg /= mstat->avg_count; + +#if 0 + g_print ("%s: count=%ld avg=%ld min=%ld max=%ld\n", + (gchar*)k, + ((SysprofMarkStat *)v)->count, + ((SysprofMarkStat *)v)->avg, + ((SysprofMarkStat *)v)->min, + ((SysprofMarkStat *)v)->max); +#endif + } + } + + g_object_set_data_full (G_OBJECT (task), + "MARK_STAT", + g_steal_pointer (&marks), + (GDestroyNotify) g_array_unref); + + g_atomic_int_set (&priv->flags, flags); + sysprof_capture_reader_reset (reader); + sysprof_capture_reader_set_stat (reader, &st); + + g_task_return_boolean (task, TRUE); +} + +static void +sysprof_display_scan_async (SysprofDisplay *self, + SysprofCaptureReader *reader, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_autoptr(GTask) task = NULL; + + g_return_if_fail (SYSPROF_IS_DISPLAY (self)); + g_return_if_fail (reader != NULL); + g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, sysprof_display_scan_async); + g_task_set_task_data (task, + sysprof_capture_reader_ref (reader), + (GDestroyNotify) sysprof_capture_reader_unref); + g_task_run_in_thread (task, sysprof_display_scan_worker); +} + +static gboolean +sysprof_display_scan_finish (SysprofDisplay *self, + GAsyncResult *result, + GError **error) +{ + SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); + GArray *marks; + + g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), FALSE); + g_return_val_if_fail (G_IS_TASK (result), FALSE); + + if ((marks = g_object_get_data (G_OBJECT (result), "MARK_STAT"))) + sysprof_details_page_add_marks (priv->details, + (const SysprofMarkStat *)(gpointer)marks->data, + marks->len); + + return g_task_propagate_boolean (G_TASK (result), error); +} + +static void +sysprof_display_load_present_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + SysprofDisplay *self = (SysprofDisplay *)object; + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = user_data; + + g_assert (SYSPROF_IS_DISPLAY (self)); + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (G_IS_TASK (task)); + + if (!sysprof_display_present_finish (self, result, &error)) + g_warning ("Error presenting: %s", error->message); + + g_task_return_boolean (task, TRUE); +} + +static void +sysprof_display_load_frame_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + SysprofVisualizersFrame *frame = (SysprofVisualizersFrame *)object; + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = user_data; + SysprofCaptureReader *reader; + SysprofDisplay *self; + GCancellable *cancellable; + + g_assert (SYSPROF_IS_VISUALIZERS_FRAME (frame)); + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (G_IS_TASK (task)); + + self = g_task_get_source_object (task); + reader = g_task_get_task_data (task); + cancellable = g_task_get_cancellable (task); + + if (!sysprof_visualizers_frame_load_finish (frame, result, &error)) + g_task_return_error (task, g_steal_pointer (&error)); + else + sysprof_display_present_async (self, + reader, + cancellable, + sysprof_display_load_present_cb, + g_steal_pointer (&task)); +} + +static void +sysprof_display_load_scan_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + SysprofDisplay *self = (SysprofDisplay *)object; + SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); + g_autoptr(GTask) task = user_data; + g_autoptr(GError) error = NULL; + SysprofCaptureReader *reader; + SysprofSelection *selection; + GCancellable *cancellable; + GList *pages; + + g_assert (SYSPROF_IS_DISPLAY (self)); + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (G_IS_TASK (task)); + + reader = g_task_get_task_data (task); + cancellable = g_task_get_cancellable (task); + + if (!sysprof_display_scan_finish (self, result, &error)) + g_task_return_error (task, g_steal_pointer (&error)); + else + sysprof_visualizers_frame_load_async (priv->visualizers, + reader, + cancellable, + sysprof_display_load_frame_cb, + g_steal_pointer (&task)); + + selection = sysprof_visualizers_frame_get_selection (priv->visualizers); + + sysprof_details_page_set_reader (priv->details, reader); + + /* Opportunistically load pages */ + pages = gtk_container_get_children (GTK_CONTAINER (priv->pages)); + for (const GList *iter = pages; iter; iter = iter->next) + { + if (SYSPROF_IS_PAGE (iter->data)) + sysprof_page_load_async (iter->data, + reader, + selection, + priv->filter, + NULL, NULL, NULL); + } + g_list_free (pages); + + gtk_stack_set_visible_child_name (priv->stack, "view"); +} + +void +sysprof_display_load_async (SysprofDisplay *self, + SysprofCaptureReader *reader, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail (SYSPROF_IS_DISPLAY (self)); + g_return_if_fail (reader != NULL); + g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + + if (priv->reader != reader) + { + g_clear_pointer (&priv->reader, sysprof_capture_reader_unref); + priv->reader = sysprof_capture_reader_ref (reader); + } + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, sysprof_display_load_async); + g_task_set_task_data (task, + sysprof_capture_reader_ref (reader), + (GDestroyNotify) sysprof_capture_reader_unref); + + /* First scan the reader for any sort of data we care about before + * we notify aids to load content. That allows us to ensure we have + * proper timing data for the consumers. + */ + sysprof_display_scan_async (self, + reader, + cancellable, + sysprof_display_load_scan_cb, + g_steal_pointer (&task)); +} + +gboolean +sysprof_display_load_finish (SysprofDisplay *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), FALSE); + g_return_val_if_fail (G_IS_TASK (result), FALSE); + + return g_task_propagate_boolean (G_TASK (result), error); +} + +SysprofZoomManager * +sysprof_display_get_zoom_manager (SysprofDisplay *self) { SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), NULL); - return priv->profiler; + return sysprof_visualizers_frame_get_zoom_manager (priv->visualizers); } /** @@ -445,60 +1009,7 @@ sysprof_display_is_empty (SysprofDisplay *self) return priv->file == NULL && priv->profiler == NULL && gtk_stack_get_visible_child (priv->stack) == GTK_WIDGET (priv->assistant) && - NULL == sysprof_capture_view_get_reader (priv->capture_view); -} - -static void -sysprof_display_open_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - g_autoptr(SysprofDisplay) self = user_data; - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - g_autoptr(SysprofCaptureReader) reader = NULL; - g_autoptr(GError) error = NULL; - - g_assert (SYSPROF_IS_DISPLAY (self)); - g_assert (G_IS_TASK (result)); - - if (!(reader = g_task_propagate_pointer (G_TASK (result), &error))) - { - gtk_stack_set_visible_child (priv->stack, GTK_WIDGET (priv->failed_view)); - return; - } - - sysprof_capture_view_load_async (priv->capture_view, - reader, - NULL, - (GAsyncReadyCallback) sysprof_display_load_cb, - g_object_ref (self)); - gtk_stack_set_visible_child (priv->stack, GTK_WIDGET (priv->capture_view)); -} - -static void -sysprof_display_open_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - g_autofree gchar *path = NULL; - g_autoptr(GError) error = NULL; - SysprofCaptureReader *reader; - GFile *file = task_data; - - g_assert (G_IS_TASK (task)); - g_assert (source_object == NULL); - g_assert (G_IS_FILE (file)); - g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); - - path = g_file_get_path (file); - - if (!(reader = sysprof_capture_reader_new (path, &error))) - g_task_return_error (task, g_steal_pointer ((&error))); - else - g_task_return_pointer (task, - g_steal_pointer (&reader), - (GDestroyNotify) sysprof_capture_reader_unref); + NULL == priv->reader; } void @@ -506,7 +1017,10 @@ sysprof_display_open (SysprofDisplay *self, GFile *file) { SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); + g_autoptr(SysprofCaptureReader) reader = NULL; + g_autoptr(GError) error = NULL; g_autoptr(GTask) task = NULL; + g_autofree gchar *path = NULL; g_return_if_fail (SYSPROF_IS_DISPLAY (self)); g_return_if_fail (G_IS_FILE (file)); @@ -515,77 +1029,16 @@ sysprof_display_open (SysprofDisplay *self, g_set_object (&priv->file, file); - task = g_task_new (NULL, NULL, sysprof_display_open_cb, g_object_ref (self)); - g_task_set_task_data (task, g_file_dup (file), g_object_unref); - g_task_run_in_thread (task, sysprof_display_open_worker); + path = g_file_get_path (file); + + if (!(reader = sysprof_capture_reader_new (path, &error))) + g_warning ("Failed to open capture: %s", error->message); + else + sysprof_display_load_async (self, reader, NULL, NULL, NULL); update_title_child_property (self); } -void -sysprof_display_save (SysprofDisplay *self) -{ - SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); - g_autoptr(GFile) file = NULL; - GtkFileChooserNative *native; - SysprofCaptureReader *reader; - GtkWindow *parent; - gint res; - - g_return_if_fail (SYSPROF_IS_DISPLAY (self)); - - if (!(reader = sysprof_capture_view_get_reader (priv->capture_view))) - return; - - parent = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))); - - native = gtk_file_chooser_native_new (_("Save Recording"), - parent, - GTK_FILE_CHOOSER_ACTION_SAVE, - _("Save"), - _("Cancel")); - gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (native), TRUE); - gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (native), TRUE); - gtk_file_chooser_set_create_folders (GTK_FILE_CHOOSER (native), TRUE); - gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (native), "capture.syscap"); - - res = gtk_native_dialog_run (GTK_NATIVE_DIALOG (native)); - - switch (res) - { - case GTK_RESPONSE_ACCEPT: - file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (native)); - - if (g_file_is_native (file)) - { - g_autofree gchar *path = g_file_get_path (file); - g_autoptr(GError) error = NULL; - - if (!sysprof_capture_reader_save_as (reader, path, &error)) - { - GtkWidget *msg; - - msg = gtk_message_dialog_new (parent, - GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_USE_HEADER_BAR, - GTK_MESSAGE_ERROR, - GTK_BUTTONS_CLOSE, - _("Failed to save recording: %s"), - error->message); - gtk_window_present (GTK_WINDOW (msg)); - g_signal_connect (msg, "response", G_CALLBACK (gtk_widget_destroy), NULL); - } - } - - break; - - default: - break; - } - - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]); - gtk_native_dialog_destroy (GTK_NATIVE_DIALOG (native)); -} - gboolean sysprof_display_get_can_save (SysprofDisplay *self) { @@ -593,7 +1046,7 @@ sysprof_display_get_can_save (SysprofDisplay *self) g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), FALSE); - return sysprof_capture_view_get_reader (priv->capture_view) != NULL; + return priv->reader != NULL; } void @@ -625,7 +1078,8 @@ sysprof_display_get_can_replay (SysprofDisplay *self) g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), FALSE); return !sysprof_display_is_empty (self) && - sysprof_capture_view_get_can_replay (priv->capture_view); + priv->reader != NULL && + !!(priv->flags & SYSPROF_CAPTURE_FLAGS_CAN_REPLAY); } /** @@ -645,15 +1099,12 @@ sysprof_display_replay (SysprofDisplay *self) { SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); g_autoptr(SysprofProfiler) profiler = NULL; - SysprofCaptureReader *reader; SysprofDisplay *copy; g_return_val_if_fail (SYSPROF_IS_DISPLAY (self), NULL); + g_return_val_if_fail (priv->reader != NULL, NULL); - reader = sysprof_capture_view_get_reader (priv->capture_view); - g_return_val_if_fail (reader != NULL, NULL); - - profiler = sysprof_local_profiler_new_replay (reader); + profiler = sysprof_local_profiler_new_replay (priv->reader); g_return_val_if_fail (profiler != NULL, NULL); g_return_val_if_fail (SYSPROF_IS_LOCAL_PROFILER (profiler), NULL); @@ -676,3 +1127,64 @@ sysprof_display_new_for_profiler (SysprofProfiler *profiler) return GTK_WIDGET (g_steal_pointer (&self)); } + +void +sysprof_display_save (SysprofDisplay *self) +{ + SysprofDisplayPrivate *priv = sysprof_display_get_instance_private (self); + g_autoptr(GFile) file = NULL; + GtkFileChooserNative *native; + GtkWindow *parent; + gint res; + + g_return_if_fail (SYSPROF_IS_DISPLAY (self)); + g_return_if_fail (priv->reader != NULL); + + parent = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))); + + native = gtk_file_chooser_native_new (_("Save Recording"), + parent, + GTK_FILE_CHOOSER_ACTION_SAVE, + _("Save"), + _("Cancel")); + gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (native), TRUE); + gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (native), TRUE); + gtk_file_chooser_set_create_folders (GTK_FILE_CHOOSER (native), TRUE); + gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (native), "capture.syscap"); + + res = gtk_native_dialog_run (GTK_NATIVE_DIALOG (native)); + + switch (res) + { + case GTK_RESPONSE_ACCEPT: + file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (native)); + + if (g_file_is_native (file)) + { + g_autofree gchar *path = g_file_get_path (file); + g_autoptr(GError) error = NULL; + + if (!sysprof_capture_reader_save_as (priv->reader, path, &error)) + { + GtkWidget *msg; + + msg = gtk_message_dialog_new (parent, + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_USE_HEADER_BAR, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + _("Failed to save recording: %s"), + error->message); + gtk_window_present (GTK_WINDOW (msg)); + g_signal_connect (msg, "response", G_CALLBACK (gtk_widget_destroy), NULL); + } + } + + break; + + default: + break; + } + + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]); + gtk_native_dialog_destroy (GTK_NATIVE_DIALOG (native)); +} diff --git a/src/libsysprof-ui/sysprof-display.h b/src/libsysprof-ui/sysprof-display.h index 96d780bd..7f0ea9cc 100644 --- a/src/libsysprof-ui/sysprof-display.h +++ b/src/libsysprof-ui/sysprof-display.h @@ -23,7 +23,9 @@ #include #include -#include "sysprof-version-macros.h" +#include "sysprof-page.h" +#include "sysprof-visualizer-group.h" +#include "sysprof-zoom-manager.h" G_BEGIN_DECLS @@ -32,38 +34,59 @@ G_BEGIN_DECLS SYSPROF_AVAILABLE_IN_ALL G_DECLARE_DERIVABLE_TYPE (SysprofDisplay, sysprof_display, SYSPROF, DISPLAY, GtkBin) -SYSPROF_ALIGNED_BEGIN(8) struct _SysprofDisplayClass { GtkBinClass parent_class; /*< private >*/ gpointer _reserved[16]; -} -SYSPROF_ALIGNED_END(8); +}; SYSPROF_AVAILABLE_IN_ALL -GtkWidget *sysprof_display_new (void); +GtkWidget *sysprof_display_new (void); SYSPROF_AVAILABLE_IN_ALL -GtkWidget *sysprof_display_new_for_profiler (SysprofProfiler *profiler); +GtkWidget *sysprof_display_new_for_profiler (SysprofProfiler *profiler); SYSPROF_AVAILABLE_IN_ALL -gchar *sysprof_display_dup_title (SysprofDisplay *self); +char *sysprof_display_dup_title (SysprofDisplay *self); SYSPROF_AVAILABLE_IN_ALL -SysprofProfiler *sysprof_display_get_profiler (SysprofDisplay *self); +SysprofProfiler *sysprof_display_get_profiler (SysprofDisplay *self); SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_display_is_empty (SysprofDisplay *self); +void sysprof_display_add_group (SysprofDisplay *self, + SysprofVisualizerGroup *group); SYSPROF_AVAILABLE_IN_ALL -void sysprof_display_open (SysprofDisplay *self, - GFile *file); +void sysprof_display_add_page (SysprofDisplay *self, + SysprofPage *page); SYSPROF_AVAILABLE_IN_ALL -void sysprof_display_save (SysprofDisplay *self); +SysprofPage *sysprof_display_get_visible_page (SysprofDisplay *self); SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_display_get_can_save (SysprofDisplay *self); +void sysprof_display_set_visible_page (SysprofDisplay *self, + SysprofPage *page); SYSPROF_AVAILABLE_IN_ALL -void sysprof_display_stop_recording (SysprofDisplay *self); +SysprofZoomManager *sysprof_display_get_zoom_manager (SysprofDisplay *self); SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_display_get_can_replay (SysprofDisplay *self); +void sysprof_display_load_async (SysprofDisplay *self, + SysprofCaptureReader *reader, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); SYSPROF_AVAILABLE_IN_ALL -SysprofDisplay *sysprof_display_replay (SysprofDisplay *self); +gboolean sysprof_display_load_finish (SysprofDisplay *self, + GAsyncResult *result, + GError **error); +SYSPROF_AVAILABLE_IN_ALL +gboolean sysprof_display_is_empty (SysprofDisplay *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_display_open (SysprofDisplay *self, + GFile *file); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_display_save (SysprofDisplay *self); +SYSPROF_AVAILABLE_IN_ALL +gboolean sysprof_display_get_can_save (SysprofDisplay *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_display_stop_recording (SysprofDisplay *self); +SYSPROF_AVAILABLE_IN_ALL +gboolean sysprof_display_get_can_replay (SysprofDisplay *self); +SYSPROF_AVAILABLE_IN_ALL +SysprofDisplay *sysprof_display_replay (SysprofDisplay *self); G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-display.ui b/src/libsysprof-ui/sysprof-display.ui index a9af096f..43661e6a 100644 --- a/src/libsysprof-ui/sysprof-display.ui +++ b/src/libsysprof-ui/sysprof-display.ui @@ -14,11 +14,40 @@ - + + vertical true + + + false + true + + + + + horizontal + true + + + + + false + true + true + + + true + + + Details + details + + + + - capture + view @@ -26,14 +55,11 @@ true - recording + record - - computer-fail-symbolic - Something went wrong - Sysprof failed to access the requested performance data. + true diff --git a/src/libsysprof-ui/sysprof-empty-state-view.h b/src/libsysprof-ui/sysprof-empty-state-view.h index 9e7aa72c..4c15aecf 100644 --- a/src/libsysprof-ui/sysprof-empty-state-view.h +++ b/src/libsysprof-ui/sysprof-empty-state-view.h @@ -20,10 +20,6 @@ #pragma once -#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION) -# error "Only can be included directly." -#endif - #include #include @@ -31,7 +27,6 @@ G_BEGIN_DECLS #define SYSPROF_TYPE_EMPTY_STATE_VIEW (sysprof_empty_state_view_get_type()) -SYSPROF_AVAILABLE_IN_ALL G_DECLARE_DERIVABLE_TYPE (SysprofEmptyStateView, sysprof_empty_state_view, SYSPROF, EMPTY_STATE_VIEW, GtkBin) struct _SysprofEmptyStateViewClass @@ -41,7 +36,6 @@ struct _SysprofEmptyStateViewClass gpointer padding[4]; }; -SYSPROF_AVAILABLE_IN_ALL GtkWidget *sysprof_empty_state_view_new (void); G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-failed-state-view.h b/src/libsysprof-ui/sysprof-failed-state-view.h index ad14efcf..91ed603f 100644 --- a/src/libsysprof-ui/sysprof-failed-state-view.h +++ b/src/libsysprof-ui/sysprof-failed-state-view.h @@ -20,10 +20,6 @@ #pragma once -#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION) -# error "Only can be included directly." -#endif - #include #include @@ -31,7 +27,6 @@ G_BEGIN_DECLS #define SYSPROF_TYPE_FAILED_STATE_VIEW (sysprof_failed_state_view_get_type()) -SYSPROF_AVAILABLE_IN_ALL G_DECLARE_DERIVABLE_TYPE (SysprofFailedStateView, sysprof_failed_state_view, SYSPROF, FAILED_STATE_VIEW, GtkBin) struct _SysprofFailedStateViewClass @@ -41,9 +36,7 @@ struct _SysprofFailedStateViewClass gpointer padding[4]; }; -SYSPROF_AVAILABLE_IN_ALL GtkWidget *sysprof_failed_state_view_new (void); -SYSPROF_AVAILABLE_IN_ALL void sysprof_failed_state_view_set_profiler (SysprofFailedStateView *self, SysprofProfiler *profiler); diff --git a/src/libsysprof-ui/sysprof-line-visualizer-row.h b/src/libsysprof-ui/sysprof-line-visualizer-row.h deleted file mode 100644 index d27b1958..00000000 --- a/src/libsysprof-ui/sysprof-line-visualizer-row.h +++ /dev/null @@ -1,70 +0,0 @@ -/* sysprof-line-visualizer-row.h - * - * Copyright 2016-2019 Christian Hergert - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -#pragma once - -#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION) -# error "Only can be included directly." -#endif - -#include - -#include "sysprof-visualizer-row.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_LINE_VISUALIZER_ROW (sysprof_line_visualizer_row_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_DERIVABLE_TYPE (SysprofLineVisualizerRow, sysprof_line_visualizer_row, SYSPROF, LINE_VISUALIZER_ROW, SysprofVisualizerRow) - -struct _SysprofLineVisualizerRowClass -{ - SysprofVisualizerRowClass parent_class; - - void (*counter_added) (SysprofLineVisualizerRow *self, - guint counter_id); - - /*< private >*/ - gpointer _reserved[16]; -}; - -SYSPROF_AVAILABLE_IN_ALL -GtkWidget *sysprof_line_visualizer_row_new (void); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_line_visualizer_row_clear (SysprofLineVisualizerRow *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_line_visualizer_row_add_counter (SysprofLineVisualizerRow *self, - guint counter_id, - const GdkRGBA *color); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_line_visualizer_row_set_line_width (SysprofLineVisualizerRow *self, - guint counter_id, - gdouble width); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_line_visualizer_row_set_fill (SysprofLineVisualizerRow *self, - guint counter_id, - const GdkRGBA *color); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_line_visualizer_row_set_dash (SysprofLineVisualizerRow *self, - guint counter_id, - gboolean use_dash); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-line-visualizer-row.c b/src/libsysprof-ui/sysprof-line-visualizer.c similarity index 62% rename from src/libsysprof-ui/sysprof-line-visualizer-row.c rename to src/libsysprof-ui/sysprof-line-visualizer.c index 670f84f9..1b0295e4 100644 --- a/src/libsysprof-ui/sysprof-line-visualizer-row.c +++ b/src/libsysprof-ui/sysprof-line-visualizer.c @@ -1,4 +1,4 @@ -/* sysprof-line-visualizer-row.c +/* sysprof-line-visualizer.c * * Copyright 2016-2019 Christian Hergert * @@ -18,7 +18,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -#define G_LOG_DOMAIN "sysprof-line-visualizer-row" +#define G_LOG_DOMAIN "sysprof-line-visualizer" #include "config.h" @@ -27,7 +27,7 @@ #include #include "pointcache.h" -#include "sysprof-line-visualizer-row.h" +#include "sysprof-line-visualizer.h" typedef struct { @@ -49,11 +49,6 @@ typedef struct */ PointCache *cache; - /* - * Child widget to display the label in the upper corner. - */ - GtkLabel *label; - /* * Range of the scale for lower and upper. */ @@ -69,7 +64,7 @@ typedef struct guint y_lower_set : 1; guint y_upper_set : 1; -} SysprofLineVisualizerRowPrivate; +} SysprofLineVisualizerPrivate; typedef struct { @@ -96,19 +91,18 @@ typedef struct guint y_upper_set : 1; } LoadData; -G_DEFINE_TYPE_WITH_PRIVATE (SysprofLineVisualizerRow, sysprof_line_visualizer_row, SYSPROF_TYPE_VISUALIZER_ROW) +G_DEFINE_TYPE_WITH_PRIVATE (SysprofLineVisualizer, sysprof_line_visualizer, SYSPROF_TYPE_VISUALIZER) -static void sysprof_line_visualizer_row_load_data_async (SysprofLineVisualizerRow *self, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -static PointCache *sysprof_line_visualizer_row_load_data_finish (SysprofLineVisualizerRow *self, - GAsyncResult *result, - GError **error); +static void sysprof_line_visualizer_load_data_async (SysprofLineVisualizer *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +static PointCache *sysprof_line_visualizer_load_data_finish (SysprofLineVisualizer *self, + GAsyncResult *result, + GError **error); enum { PROP_0, - PROP_TITLE, PROP_Y_LOWER, PROP_Y_UPPER, N_PROPS @@ -144,34 +138,38 @@ copy_array (GArray *ar) } static gboolean -sysprof_line_visualizer_row_draw (GtkWidget *widget, - cairo_t *cr) +sysprof_line_visualizer_draw (GtkWidget *widget, + cairo_t *cr) { - SysprofLineVisualizerRow *self = (SysprofLineVisualizerRow *)widget; - SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self); + SysprofLineVisualizer *self = (SysprofLineVisualizer *)widget; + SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); GtkStyleContext *style_context; GtkStateFlags flags; GtkAllocation alloc; + GdkRectangle clip; GdkRGBA foreground; gboolean ret; - g_assert (SYSPROF_IS_LINE_VISUALIZER_ROW (widget)); + g_assert (SYSPROF_IS_LINE_VISUALIZER (widget)); g_assert (cr != NULL); gtk_widget_get_allocation (widget, &alloc); - ret = GTK_WIDGET_CLASS (sysprof_line_visualizer_row_parent_class)->draw (widget, cr); + ret = GTK_WIDGET_CLASS (sysprof_line_visualizer_parent_class)->draw (widget, cr); if (priv->cache == NULL) return ret; + if (!gdk_cairo_get_clip_rectangle (cr, &clip)) + return ret; + style_context = gtk_widget_get_style_context (widget); flags = gtk_widget_get_state_flags (widget); gtk_style_context_get_color (style_context, flags, &foreground); for (guint line = 0; line < priv->lines->len; line++) { - g_autofree SysprofVisualizerRowAbsolutePoint *points = NULL; + g_autofree SysprofVisualizerAbsolutePoint *points = NULL; const LineInfo *line_info = &g_array_index (priv->lines, LineInfo, line); const Point *fpoints; guint n_fpoints = 0; @@ -181,19 +179,32 @@ sysprof_line_visualizer_row_draw (GtkWidget *widget, if (n_fpoints > 0) { - gdouble last_x; - gdouble last_y; + gdouble last_x = 0; + gdouble last_y = 0; + guint p; - points = g_new0 (SysprofVisualizerRowAbsolutePoint, n_fpoints); + points = g_new0 (SysprofVisualizerAbsolutePoint, n_fpoints); - sysprof_visualizer_row_translate_points (SYSPROF_VISUALIZER_ROW (self), - (const SysprofVisualizerRowRelativePoint *)fpoints, - n_fpoints, - points, - n_fpoints); + sysprof_visualizer_translate_points (SYSPROF_VISUALIZER (self), + (const SysprofVisualizerRelativePoint *)fpoints, + n_fpoints, + points, + n_fpoints); - last_x = points[0].x; - last_y = points[0].y; + for (p = 0; p < n_fpoints; p++) + { + if (points[p].x >= clip.x) + break; + } + + if (p >= n_fpoints) + return ret; + + if (p > 0) + p--; + + last_x = points[p].x; + last_y = points[p].y; if (line_info->fill) { @@ -205,7 +216,7 @@ sysprof_line_visualizer_row_draw (GtkWidget *widget, cairo_move_to (cr, last_x, last_y); } - for (guint i = 1; i < n_fpoints; i++) + for (guint i = p + 1; i < n_fpoints; i++) { cairo_curve_to (cr, last_x + ((points[i].x - last_x) / 2), @@ -214,8 +225,12 @@ sysprof_line_visualizer_row_draw (GtkWidget *widget, points[i].y, points[i].x, points[i].y); + last_x = points[i].x; last_y = points[i].y; + + if (points[i].x > clip.x + clip.width) + break; } if (line_info->fill) @@ -249,18 +264,18 @@ sysprof_line_visualizer_row_draw (GtkWidget *widget, } static void -sysprof_line_visualizer_row_load_data_cb (GObject *object, +sysprof_line_visualizer_load_data_cb (GObject *object, GAsyncResult *result, gpointer user_data) { - SysprofLineVisualizerRow *self = (SysprofLineVisualizerRow *)object; - SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self); + SysprofLineVisualizer *self = (SysprofLineVisualizer *)object; + SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); g_autoptr(GError) error = NULL; g_autoptr(PointCache) cache = NULL; - g_assert (SYSPROF_IS_LINE_VISUALIZER_ROW (self)); + g_assert (SYSPROF_IS_LINE_VISUALIZER (self)); - cache = sysprof_line_visualizer_row_load_data_finish (self, result, &error); + cache = sysprof_line_visualizer_load_data_finish (self, result, &error); if (cache == NULL) { @@ -275,20 +290,20 @@ sysprof_line_visualizer_row_load_data_cb (GObject *object, } static gboolean -sysprof_line_visualizer_row_do_reload (gpointer data) +sysprof_line_visualizer_do_reload (gpointer data) { - SysprofLineVisualizerRow *self = data; - SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self); + SysprofLineVisualizer *self = data; + SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); - g_assert (SYSPROF_IS_LINE_VISUALIZER_ROW (self)); + g_assert (SYSPROF_IS_LINE_VISUALIZER (self)); priv->queued_load = 0; if (priv->reader != NULL) { - sysprof_line_visualizer_row_load_data_async (self, + sysprof_line_visualizer_load_data_async (self, NULL, - sysprof_line_visualizer_row_load_data_cb, + sysprof_line_visualizer_load_data_cb, NULL); } @@ -296,29 +311,29 @@ sysprof_line_visualizer_row_do_reload (gpointer data) } static void -sysprof_line_visualizer_row_queue_reload (SysprofLineVisualizerRow *self) +sysprof_line_visualizer_queue_reload (SysprofLineVisualizer *self) { - SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self); + SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); - g_assert (SYSPROF_IS_LINE_VISUALIZER_ROW (self)); + g_assert (SYSPROF_IS_LINE_VISUALIZER (self)); if (priv->queued_load == 0) { priv->queued_load = gdk_threads_add_idle_full (G_PRIORITY_LOW, - sysprof_line_visualizer_row_do_reload, + sysprof_line_visualizer_do_reload, self, NULL); } } static void -sysprof_line_visualizer_row_set_reader (SysprofVisualizerRow *row, - SysprofCaptureReader *reader) +sysprof_line_visualizer_set_reader (SysprofVisualizer *row, + SysprofCaptureReader *reader) { - SysprofLineVisualizerRow *self = (SysprofLineVisualizerRow *)row; - SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self); + SysprofLineVisualizer *self = (SysprofLineVisualizer *)row; + SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); - g_assert (SYSPROF_IS_LINE_VISUALIZER_ROW (self)); + g_assert (SYSPROF_IS_LINE_VISUALIZER (self)); if (priv->reader != reader) { @@ -331,15 +346,15 @@ sysprof_line_visualizer_row_set_reader (SysprofVisualizerRow *row, if (reader != NULL) priv->reader = sysprof_capture_reader_ref (reader); - sysprof_line_visualizer_row_queue_reload (self); + sysprof_line_visualizer_queue_reload (self); } } static void -sysprof_line_visualizer_row_finalize (GObject *object) +sysprof_line_visualizer_finalize (GObject *object) { - SysprofLineVisualizerRow *self = (SysprofLineVisualizerRow *)object; - SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self); + SysprofLineVisualizer *self = (SysprofLineVisualizer *)object; + SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); g_clear_pointer (&priv->lines, g_array_unref); g_clear_pointer (&priv->cache, point_cache_unref); @@ -351,24 +366,20 @@ sysprof_line_visualizer_row_finalize (GObject *object) priv->queued_load = 0; } - G_OBJECT_CLASS (sysprof_line_visualizer_row_parent_class)->finalize (object); + G_OBJECT_CLASS (sysprof_line_visualizer_parent_class)->finalize (object); } static void -sysprof_line_visualizer_row_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) +sysprof_line_visualizer_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) { - SysprofLineVisualizerRow *self = SYSPROF_LINE_VISUALIZER_ROW (object); - SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self); + SysprofLineVisualizer *self = SYSPROF_LINE_VISUALIZER (object); + SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); switch (prop_id) { - case PROP_TITLE: - g_object_get_property (G_OBJECT (priv->label), "label", value); - break; - case PROP_Y_LOWER: g_value_set_double (value, priv->y_lower); break; @@ -383,20 +394,16 @@ sysprof_line_visualizer_row_get_property (GObject *object, } static void -sysprof_line_visualizer_row_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) +sysprof_line_visualizer_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) { - SysprofLineVisualizerRow *self = SYSPROF_LINE_VISUALIZER_ROW (object); - SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self); + SysprofLineVisualizer *self = SYSPROF_LINE_VISUALIZER (object); + SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); switch (prop_id) { - case PROP_TITLE: - g_object_set_property (G_OBJECT (priv->label), "label", value); - break; - case PROP_Y_LOWER: priv->y_lower = g_value_get_double (value); priv->y_lower_set = TRUE; @@ -415,26 +422,19 @@ sysprof_line_visualizer_row_set_property (GObject *object, } static void -sysprof_line_visualizer_row_class_init (SysprofLineVisualizerRowClass *klass) +sysprof_line_visualizer_class_init (SysprofLineVisualizerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - SysprofVisualizerRowClass *visualizer_class = SYSPROF_VISUALIZER_ROW_CLASS (klass); + SysprofVisualizerClass *visualizer_class = SYSPROF_VISUALIZER_CLASS (klass); - object_class->finalize = sysprof_line_visualizer_row_finalize; - object_class->get_property = sysprof_line_visualizer_row_get_property; - object_class->set_property = sysprof_line_visualizer_row_set_property; + object_class->finalize = sysprof_line_visualizer_finalize; + object_class->get_property = sysprof_line_visualizer_get_property; + object_class->set_property = sysprof_line_visualizer_set_property; - widget_class->draw = sysprof_line_visualizer_row_draw; + widget_class->draw = sysprof_line_visualizer_draw; - visualizer_class->set_reader = sysprof_line_visualizer_row_set_reader; - - properties [PROP_TITLE] = - g_param_spec_string ("title", - "Title", - "The title of the row", - NULL, - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + visualizer_class->set_reader = sysprof_line_visualizer_set_reader; properties [PROP_Y_LOWER] = g_param_spec_double ("y-lower", @@ -458,35 +458,22 @@ sysprof_line_visualizer_row_class_init (SysprofLineVisualizerRowClass *klass) } static void -sysprof_line_visualizer_row_init (SysprofLineVisualizerRow *self) +sysprof_line_visualizer_init (SysprofLineVisualizer *self) { - SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self); - PangoAttrList *attrs = pango_attr_list_new (); + SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); priv->lines = g_array_new (FALSE, FALSE, sizeof (LineInfo)); - - pango_attr_list_insert (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL * PANGO_SCALE_SMALL)); - - priv->label = g_object_new (GTK_TYPE_LABEL, - "attributes", attrs, - "visible", TRUE, - "xalign", 0.0f, - "yalign", 0.0f, - NULL); - gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (priv->label)); - - pango_attr_list_unref (attrs); } void -sysprof_line_visualizer_row_add_counter (SysprofLineVisualizerRow *self, - guint counter_id, - const GdkRGBA *color) +sysprof_line_visualizer_add_counter (SysprofLineVisualizer *self, + guint counter_id, + const GdkRGBA *color) { - SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self); + SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); LineInfo line_info = { 0 }; - g_assert (SYSPROF_IS_LINE_VISUALIZER_ROW (self)); + g_assert (SYSPROF_IS_LINE_VISUALIZER (self)); g_assert (priv->lines != NULL); line_info.id = counter_id; @@ -506,18 +493,18 @@ sysprof_line_visualizer_row_add_counter (SysprofLineVisualizerRow *self, g_array_append_val (priv->lines, line_info); - if (SYSPROF_LINE_VISUALIZER_ROW_GET_CLASS (self)->counter_added) - SYSPROF_LINE_VISUALIZER_ROW_GET_CLASS (self)->counter_added (self, counter_id); + if (SYSPROF_LINE_VISUALIZER_GET_CLASS (self)->counter_added) + SYSPROF_LINE_VISUALIZER_GET_CLASS (self)->counter_added (self, counter_id); - sysprof_line_visualizer_row_queue_reload (self); + sysprof_line_visualizer_queue_reload (self); } void -sysprof_line_visualizer_row_clear (SysprofLineVisualizerRow *self) +sysprof_line_visualizer_clear (SysprofLineVisualizer *self) { - SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self); + SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); - g_return_if_fail (SYSPROF_IS_LINE_VISUALIZER_ROW (self)); + g_return_if_fail (SYSPROF_IS_LINE_VISUALIZER (self)); if (priv->lines->len > 0) g_array_remove_range (priv->lines, 0, priv->lines->len); @@ -580,8 +567,8 @@ calc_y_int64 (gint64 lower, } static gboolean -sysprof_line_visualizer_row_load_data_frame_cb (const SysprofCaptureFrame *frame, - gpointer user_data) +sysprof_line_visualizer_load_data_frame_cb (const SysprofCaptureFrame *frame, + gpointer user_data) { LoadData *load = user_data; @@ -622,8 +609,8 @@ sysprof_line_visualizer_row_load_data_frame_cb (const SysprofCaptureFrame *frame } static gboolean -sysprof_line_visualizer_row_load_data_range_cb (const SysprofCaptureFrame *frame, - gpointer user_data) +sysprof_line_visualizer_load_data_range_cb (const SysprofCaptureFrame *frame, + gpointer user_data) { LoadData *load = user_data; @@ -689,16 +676,16 @@ sysprof_line_visualizer_row_load_data_range_cb (const SysprofCaptureFrame *frame } static void -sysprof_line_visualizer_row_load_data_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) +sysprof_line_visualizer_load_data_worker (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) { LoadData *load = task_data; g_autoptr(GArray) counter_ids = NULL; g_assert (G_IS_TASK (task)); - g_assert (SYSPROF_IS_LINE_VISUALIZER_ROW (source_object)); + g_assert (SYSPROF_IS_LINE_VISUALIZER (source_object)); g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); counter_ids = g_array_new (FALSE, FALSE, sizeof (guint)); @@ -716,7 +703,7 @@ sysprof_line_visualizer_row_load_data_worker (GTask *task, /* If y boundaries are not set, we need to discover them by scaning the data. */ if (!load->y_lower_set || !load->y_upper_set) { - sysprof_capture_cursor_foreach (load->cursor, sysprof_line_visualizer_row_load_data_range_cb, load); + sysprof_capture_cursor_foreach (load->cursor, sysprof_line_visualizer_load_data_range_cb, load); sysprof_capture_cursor_reset (load->cursor); /* Add extra boundary for some space above the graph line */ @@ -724,26 +711,26 @@ sysprof_line_visualizer_row_load_data_worker (GTask *task, load->y_upper = load->y_upper + ((load->y_upper - load->y_lower) * .25); } - sysprof_capture_cursor_foreach (load->cursor, sysprof_line_visualizer_row_load_data_frame_cb, load); + sysprof_capture_cursor_foreach (load->cursor, sysprof_line_visualizer_load_data_frame_cb, load); g_task_return_pointer (task, g_steal_pointer (&load->cache), (GDestroyNotify)point_cache_unref); } static void -sysprof_line_visualizer_row_load_data_async (SysprofLineVisualizerRow *self, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +sysprof_line_visualizer_load_data_async (SysprofLineVisualizer *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { - SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self); + SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); g_autoptr(GTask) task = NULL; LoadData *load; - g_assert (SYSPROF_IS_LINE_VISUALIZER_ROW (self)); + g_assert (SYSPROF_IS_LINE_VISUALIZER (self)); g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); task = g_task_new (self, cancellable, callback, user_data); g_task_set_priority (task, G_PRIORITY_LOW); - g_task_set_source_tag (task, sysprof_line_visualizer_row_load_data_async); + g_task_set_source_tag (task, sysprof_line_visualizer_load_data_async); if (priv->reader == NULL) { @@ -773,18 +760,18 @@ sysprof_line_visualizer_row_load_data_async (SysprofLineVisualizerRow *self, } g_task_set_task_data (task, load, load_data_free); - g_task_run_in_thread (task, sysprof_line_visualizer_row_load_data_worker); + g_task_run_in_thread (task, sysprof_line_visualizer_load_data_worker); } static PointCache * -sysprof_line_visualizer_row_load_data_finish (SysprofLineVisualizerRow *self, - GAsyncResult *result, - GError **error) +sysprof_line_visualizer_load_data_finish (SysprofLineVisualizer *self, + GAsyncResult *result, + GError **error) { - SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self); + SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); LoadData *state; - g_assert (SYSPROF_IS_LINE_VISUALIZER_ROW (self)); + g_assert (SYSPROF_IS_LINE_VISUALIZER (self)); g_assert (G_IS_TASK (result)); state = g_task_get_task_data (G_TASK (result)); @@ -805,13 +792,13 @@ sysprof_line_visualizer_row_load_data_finish (SysprofLineVisualizerRow *self, } void -sysprof_line_visualizer_row_set_line_width (SysprofLineVisualizerRow *self, - guint counter_id, - gdouble width) +sysprof_line_visualizer_set_line_width (SysprofLineVisualizer *self, + guint counter_id, + gdouble width) { - SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self); + SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); - g_return_if_fail (SYSPROF_IS_LINE_VISUALIZER_ROW (self)); + g_return_if_fail (SYSPROF_IS_LINE_VISUALIZER (self)); for (guint i = 0; i < priv->lines->len; i++) { @@ -820,20 +807,20 @@ sysprof_line_visualizer_row_set_line_width (SysprofLineVisualizerRow *self, if (info->id == counter_id) { info->line_width = width; - sysprof_line_visualizer_row_queue_reload (self); + sysprof_line_visualizer_queue_reload (self); break; } } } void -sysprof_line_visualizer_row_set_fill (SysprofLineVisualizerRow *self, - guint counter_id, - const GdkRGBA *color) +sysprof_line_visualizer_set_fill (SysprofLineVisualizer *self, + guint counter_id, + const GdkRGBA *color) { - SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self); + SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); - g_return_if_fail (SYSPROF_IS_LINE_VISUALIZER_ROW (self)); + g_return_if_fail (SYSPROF_IS_LINE_VISUALIZER (self)); for (guint i = 0; i < priv->lines->len; i++) { @@ -844,20 +831,20 @@ sysprof_line_visualizer_row_set_fill (SysprofLineVisualizerRow *self, info->fill = !!color; if (color != NULL) info->background = *color; - sysprof_line_visualizer_row_queue_reload (self); + sysprof_line_visualizer_queue_reload (self); break; } } } void -sysprof_line_visualizer_row_set_dash (SysprofLineVisualizerRow *self, - guint counter_id, - gboolean use_dash) +sysprof_line_visualizer_set_dash (SysprofLineVisualizer *self, + guint counter_id, + gboolean use_dash) { - SysprofLineVisualizerRowPrivate *priv = sysprof_line_visualizer_row_get_instance_private (self); + SysprofLineVisualizerPrivate *priv = sysprof_line_visualizer_get_instance_private (self); - g_return_if_fail (SYSPROF_IS_LINE_VISUALIZER_ROW (self)); + g_return_if_fail (SYSPROF_IS_LINE_VISUALIZER (self)); for (guint i = 0; i < priv->lines->len; i++) { @@ -866,7 +853,7 @@ sysprof_line_visualizer_row_set_dash (SysprofLineVisualizerRow *self, if (info->id == counter_id) { info->use_dash = !!use_dash; - sysprof_line_visualizer_row_queue_reload (self); + sysprof_line_visualizer_queue_reload (self); break; } } diff --git a/src/libsysprof-ui/sysprof-line-visualizer.h b/src/libsysprof-ui/sysprof-line-visualizer.h new file mode 100644 index 00000000..4fac8855 --- /dev/null +++ b/src/libsysprof-ui/sysprof-line-visualizer.h @@ -0,0 +1,57 @@ +/* sysprof-line-visualizer.h + * + * Copyright 2016-2019 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-visualizer.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_LINE_VISUALIZER (sysprof_line_visualizer_get_type()) + +G_DECLARE_DERIVABLE_TYPE (SysprofLineVisualizer, sysprof_line_visualizer, SYSPROF, LINE_VISUALIZER, SysprofVisualizer) + +struct _SysprofLineVisualizerClass +{ + SysprofVisualizerClass parent_class; + + void (*counter_added) (SysprofLineVisualizer *self, + guint counter_id); + + /*< private >*/ + gpointer _reserved[16]; +}; + +GtkWidget *sysprof_line_visualizer_new (void); +void sysprof_line_visualizer_clear (SysprofLineVisualizer *self); +void sysprof_line_visualizer_add_counter (SysprofLineVisualizer *self, + guint counter_id, + const GdkRGBA *color); +void sysprof_line_visualizer_set_line_width (SysprofLineVisualizer *self, + guint counter_id, + gdouble width); +void sysprof_line_visualizer_set_fill (SysprofLineVisualizer *self, + guint counter_id, + const GdkRGBA *color); +void sysprof_line_visualizer_set_dash (SysprofLineVisualizer *self, + guint counter_id, + gboolean use_dash); + +G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-logs-aid.c b/src/libsysprof-ui/sysprof-logs-aid.c new file mode 100644 index 00000000..466eab3e --- /dev/null +++ b/src/libsysprof-ui/sysprof-logs-aid.c @@ -0,0 +1,237 @@ +/* sysprof-logs-aid.c + * + * Copyright 2019 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#define G_LOG_DOMAIN "sysprof-logs-aid" + +#include "config.h" + +#include + +#include "sysprof-color-cycle.h" +#include "sysprof-logs-aid.h" +#include "sysprof-logs-page.h" +#include "sysprof-mark-visualizer.h" + +struct _SysprofLogsAid +{ + SysprofAid parent_instance; +}; + +typedef struct +{ + SysprofDisplay *display; + SysprofCaptureCursor *cursor; + GArray *log_marks; +} Present; + +G_DEFINE_TYPE (SysprofLogsAid, sysprof_logs_aid, SYSPROF_TYPE_AID) + +static void +present_free (gpointer data) +{ + Present *p = data; + + g_clear_pointer (&p->log_marks, g_array_unref); + g_clear_pointer (&p->cursor, sysprof_capture_cursor_unref); + g_clear_object (&p->display); + g_slice_free (Present, p); +} + +static void +on_group_activated_cb (SysprofVisualizerGroup *group, + SysprofPage *page) +{ + SysprofDisplay *display; + + g_assert (SYSPROF_IS_VISUALIZER_GROUP (group)); + g_assert (SYSPROF_IS_PAGE (page)); + + display = SYSPROF_DISPLAY (gtk_widget_get_ancestor (GTK_WIDGET (page), SYSPROF_TYPE_DISPLAY)); + sysprof_display_set_visible_page (display, page); +} + +/** + * sysprof_logs_aid_new: + * + * Create a new #SysprofLogsAid. + * + * Returns: (transfer full): a newly created #SysprofLogsAid + */ +SysprofAid * +sysprof_logs_aid_new (void) +{ + return g_object_new (SYSPROF_TYPE_LOGS_AID, NULL); +} + +static gboolean +find_marks_cb (const SysprofCaptureFrame *frame, + gpointer user_data) +{ + Present *p = user_data; + + g_assert (frame != NULL); + g_assert (p != NULL); + + if (frame->type == SYSPROF_CAPTURE_FRAME_LOG) + { + SysprofMarkTimeSpan span = { frame->time, frame->time }; + g_array_append_val (p->log_marks, span); + } + + return TRUE; +} + +static gint +compare_span (const SysprofMarkTimeSpan *a, + const SysprofMarkTimeSpan *b) +{ + if (a->kind < b->kind) + return -1; + + if (b->kind < a->kind) + return 1; + + if (a->begin < b->begin) + return -1; + + if (b->begin < a->begin) + return 1; + + if (b->end > a->end) + return -1; + + return 0; +} + +static void +sysprof_logs_aid_present_worker (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + Present *p = task_data; + + g_assert (G_IS_TASK (task)); + g_assert (p != NULL); + g_assert (SYSPROF_IS_DISPLAY (p->display)); + g_assert (p->cursor != NULL); + g_assert (SYSPROF_IS_LOGS_AID (source_object)); + + sysprof_capture_cursor_foreach (p->cursor, find_marks_cb, p); + g_array_sort (p->log_marks, (GCompareFunc)compare_span); + + g_task_return_boolean (task, TRUE); +} + +static void +sysprof_logs_aid_present_async (SysprofAid *aid, + SysprofCaptureReader *reader, + SysprofDisplay *display, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + static const SysprofCaptureFrameType logs[] = { + SYSPROF_CAPTURE_FRAME_LOG, + }; + SysprofLogsAid *self = (SysprofLogsAid *)aid; + g_autoptr(GTask) task = NULL; + Present p = {0}; + + g_assert (SYSPROF_IS_LOGS_AID (self)); + + p.display = g_object_ref (display); + p.log_marks = g_array_new (FALSE, FALSE, sizeof (SysprofMarkTimeSpan)); + p.cursor = sysprof_capture_cursor_new (reader); + sysprof_capture_cursor_add_condition (p.cursor, + sysprof_capture_condition_new_where_type_in (1, logs)); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, sysprof_logs_aid_present_async); + g_task_set_task_data (task, + g_slice_dup (Present, &p), + present_free); + g_task_run_in_thread (task, sysprof_logs_aid_present_worker); +} + +static gboolean +sysprof_logs_aid_present_finish (SysprofAid *aid, + GAsyncResult *result, + GError **error) +{ + Present *p; + + g_assert (SYSPROF_IS_LOGS_AID (aid)); + g_assert (G_IS_TASK (result)); + + p = g_task_get_task_data (G_TASK (result)); + + if (p->log_marks->len > 0) + { + g_autoptr(GHashTable) items = NULL; + SysprofVisualizerGroup *group; + SysprofVisualizer *marks; + SysprofPage *page; + + items = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, + (GDestroyNotify) g_array_unref); + g_hash_table_insert (items, g_strdup (_("Logs")), g_array_ref (p->log_marks)); + + group = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP, + "can-focus", TRUE, + "title", _("Logs"), + "visible", TRUE, + NULL); + + marks = sysprof_mark_visualizer_new (items); + sysprof_visualizer_set_title (marks, _("Logs")); + gtk_widget_show (GTK_WIDGET (marks)); + sysprof_visualizer_group_insert (group, marks, 0, FALSE); + sysprof_display_add_group (p->display, group); + + page = g_object_new (SYSPROF_TYPE_LOGS_PAGE, + "title", _("Logs"), + "visible", TRUE, + NULL); + sysprof_display_add_page (p->display, page); + + g_signal_connect_object (group, + "group-activated", + G_CALLBACK (on_group_activated_cb), + page, + 0); + } + + return g_task_propagate_boolean (G_TASK (result), error); +} + +static void +sysprof_logs_aid_class_init (SysprofLogsAidClass *klass) +{ + SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass); + + aid_class->present_async = sysprof_logs_aid_present_async; + aid_class->present_finish = sysprof_logs_aid_present_finish; +} + +static void +sysprof_logs_aid_init (SysprofLogsAid *self) +{ +} diff --git a/src/libsysprof-ui/sysprof-logs-aid.h b/src/libsysprof-ui/sysprof-logs-aid.h new file mode 100644 index 00000000..2f2d13ad --- /dev/null +++ b/src/libsysprof-ui/sysprof-logs-aid.h @@ -0,0 +1,33 @@ +/* sysprof-logs-aid.h + * + * Copyright 2019 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-aid.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_LOGS_AID (sysprof_logs_aid_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofLogsAid, sysprof_logs_aid, SYSPROF, LOGS_AID, SysprofAid) + +SysprofAid *sysprof_logs_aid_new (void); + +G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-logs-page.c b/src/libsysprof-ui/sysprof-logs-page.c new file mode 100644 index 00000000..f2b46af8 --- /dev/null +++ b/src/libsysprof-ui/sysprof-logs-page.c @@ -0,0 +1,116 @@ +/* sysprof-logs-page.c + * + * Copyright 2019 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#define G_LOG_DOMAIN "sysprof-logs-page" + +#include "config.h" + +#include "sysprof-log-model.h" +#include "sysprof-logs-page.h" + +struct _SysprofLogsPage +{ + SysprofPage parent_instance; + + /* Template Widgets */ + GtkTreeView *tree_view; +}; + +G_DEFINE_TYPE (SysprofLogsPage, sysprof_logs_page, SYSPROF_TYPE_PAGE) + +static void +sysprof_logs_page_load_cb (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + SysprofLogsPage *self; + g_autoptr(SysprofLogModel) model = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GTask) task = user_data; + + g_assert (G_IS_ASYNC_RESULT (result)); + g_assert (G_IS_TASK (task)); + + if (!(model = sysprof_log_model_new_finish (result, &error))) + g_task_return_error (task, g_steal_pointer (&error)); + else + g_task_return_boolean (task, TRUE); + + self = g_task_get_source_object (task); + + gtk_tree_view_set_model (self->tree_view, GTK_TREE_MODEL (model)); +} + +static void +sysprof_logs_page_load_async (SysprofPage *page, + SysprofCaptureReader *reader, + SysprofSelection *selection, + SysprofCaptureCondition *filter, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + SysprofLogsPage *self = (SysprofLogsPage *)page; + g_autoptr(GTask) task = NULL; + + g_assert (SYSPROF_IS_LOGS_PAGE (self)); + g_assert (reader != NULL); + g_assert (!selection || SYSPROF_IS_SELECTION (selection)); + g_assert (!cancellable || G_IS_CANCELLABLE (cancellable)); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, sysprof_logs_page_load_async); + + sysprof_log_model_new_async (reader, + selection, + cancellable, + sysprof_logs_page_load_cb, + g_steal_pointer (&task)); +} + +static gboolean +sysprof_logs_page_load_finish (SysprofPage *page, + GAsyncResult *result, + GError **error) +{ + g_assert (SYSPROF_IS_LOGS_PAGE (page)); + g_assert (G_IS_TASK (result)); + + return g_task_propagate_boolean (G_TASK (result), error); +} + +static void +sysprof_logs_page_class_init (SysprofLogsPageClass *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + SysprofPageClass *page_class = SYSPROF_PAGE_CLASS (klass); + + page_class->load_async = sysprof_logs_page_load_async; + page_class->load_finish = sysprof_logs_page_load_finish; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-logs-page.ui"); + gtk_widget_class_bind_template_child (widget_class, SysprofLogsPage, tree_view); +} + +static void +sysprof_logs_page_init (SysprofLogsPage *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); +} diff --git a/src/libsysprof-ui/sysprof-visualizer-row-private.h b/src/libsysprof-ui/sysprof-logs-page.h similarity index 74% rename from src/libsysprof-ui/sysprof-visualizer-row-private.h rename to src/libsysprof-ui/sysprof-logs-page.h index a364056b..97bb2455 100644 --- a/src/libsysprof-ui/sysprof-visualizer-row-private.h +++ b/src/libsysprof-ui/sysprof-logs-page.h @@ -1,6 +1,6 @@ -/* sysprof-visualizer-row-private.h +/* sysprof-logs-page.h * - * Copyright 2016-2019 Christian Hergert + * Copyright 2019 Christian Hergert * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,10 +20,12 @@ #pragma once -#include "sysprof-visualizer-row.h" +#include "sysprof-page.h" G_BEGIN_DECLS -gint _sysprof_visualizer_row_get_graph_width (SysprofVisualizerRow *self); +#define SYSPROF_TYPE_LOGS_PAGE (sysprof_logs_page_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofLogsPage, sysprof_logs_page, SYSPROF, LOGS_PAGE, SysprofPage) G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-logs-page.ui b/src/libsysprof-ui/sysprof-logs-page.ui new file mode 100644 index 00000000..6760253a --- /dev/null +++ b/src/libsysprof-ui/sysprof-logs-page.ui @@ -0,0 +1,75 @@ + + + + diff --git a/src/libsysprof-ui/sysprof-mark-visualizer-row.c b/src/libsysprof-ui/sysprof-mark-visualizer-row.c deleted file mode 100644 index b853866e..00000000 --- a/src/libsysprof-ui/sysprof-mark-visualizer-row.c +++ /dev/null @@ -1,492 +0,0 @@ -/* sysprof-mark-visualizer-row.c - * - * Copyright 2018-2019 Christian Hergert - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -#define G_LOG_DOMAIN "sysprof-mark-visualizer-row" - -#include "config.h" - -#include - -#include "sysprof-capture-condition.h" -#include "sysprof-capture-cursor.h" -#include "rectangles.h" -#include "sysprof-mark-visualizer-row.h" - -typedef struct -{ - /* - * Our reader as assigned by the visualizer system. - */ - SysprofCaptureReader *reader; - - /* - * The group we care about for displaying marks. The idea is that we only - * show one group per-row, so tooling from separate systems can either be - * displayed together or not, based on usefulness. - */ - gchar *group; - - /* - * Rectangle information we have built from the marks that belong to this - * row of information. - */ - Rectangles *rectangles; - - /* - * Child widget to display the label in the upper corner. - */ - GtkLabel *label; -} SysprofMarkVisualizerRowPrivate; - -typedef struct -{ - gchar *group; - SysprofCaptureCursor *cursor; - Rectangles *rects; - GHashTable *inferred_rects; -} BuildState; - -typedef struct { - gint64 time; - gchar *name; - gchar *message; -} InferredRect; - -enum { - PROP_0, - PROP_GROUP, - PROP_TITLE, - N_PROPS -}; - -G_DEFINE_TYPE_WITH_PRIVATE (SysprofMarkVisualizerRow, sysprof_mark_visualizer_row, SYSPROF_TYPE_VISUALIZER_ROW) - -static GParamSpec *properties [N_PROPS]; - -static void -free_inferred_rect (InferredRect *rect) -{ - g_free (rect->name); - g_free (rect->message); - g_slice_free (InferredRect, rect); -} - -static void -add_inferred_rect_point (BuildState *state, - InferredRect *rect) -{ - rectangles_add (state->rects, - rect->time, - rect->time, - rect->name, - rect->message); -} - -static void -build_state_free (BuildState *state) -{ - g_hash_table_remove_all (state->inferred_rects); - g_clear_pointer (&state->inferred_rects, g_hash_table_unref); - g_free (state->group); - g_clear_pointer (&state->cursor, sysprof_capture_cursor_unref); - g_slice_free (BuildState, state); -} - -/* Creates rectangles for GPU marks. - * - * GPU marks come in as a begin and an end, but since those things are - * processessed on potentially different CPUs, perf doesn't record - * them in sequence order in the mmap ringbuffer. Thus, we have to - * shuffle things back around at visualization time. - */ -static gboolean -process_gpu_mark (BuildState *state, - const SysprofCaptureMark *mark) -{ - InferredRect *rect = g_hash_table_lookup (state->inferred_rects, - mark->message); - - if (rect) - { - gboolean ours_begins = strstr (mark->name, "begin") != NULL; - gboolean theirs_begins = strstr (rect->name, "begin") != NULL; - - if (ours_begins != theirs_begins) - { - rectangles_add (state->rects, - ours_begins ? mark->frame.time : rect->time, - ours_begins ? rect->time : mark->frame.time, - ours_begins ? mark->name : rect->name, - rect->message); - } - else - { - /* Something went weird with the tracking (GPU hang caused - * two starts?), so just put up both time points as vertical - * bars for now. - */ - rectangles_add (state->rects, - mark->frame.time, - mark->frame.time, - mark->name, - mark->message); - - add_inferred_rect_point (state, rect); - } - - g_hash_table_remove (state->inferred_rects, - rect->message); - } - else - { - rect = g_slice_new0 (InferredRect); - rect->name = g_strdup (mark->name); - rect->message = g_strdup (mark->message); - rect->time = mark->frame.time; - - g_hash_table_insert (state->inferred_rects, rect->message, rect); - } - - return TRUE; -} - - -static gboolean -sysprof_mark_visualizer_row_add_rect (const SysprofCaptureFrame *frame, - gpointer user_data) -{ - BuildState *state = user_data; - const SysprofCaptureMark *mark = (const SysprofCaptureMark *)frame; - - g_assert (frame != NULL); - g_assert (frame->type == SYSPROF_CAPTURE_FRAME_MARK); - g_assert (state != NULL); - g_assert (state->rects != NULL); - - if (g_strcmp0 (mark->group, state->group) == 0) - { - if (strstr (mark->name, "gpu begin") != NULL || - strstr (mark->name, "gpu end") != NULL) - process_gpu_mark (state, mark); - else - rectangles_add (state->rects, - frame->time, - frame->time + mark->duration, - mark->name, - mark->message); - } - - return TRUE; -} - -static void -sysprof_mark_visualizer_row_worker (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - BuildState *state = task_data; - GHashTableIter iter; - gpointer key, value; - gint64 end_time; - - g_assert (G_IS_TASK (task)); - g_assert (SYSPROF_IS_MARK_VISUALIZER_ROW (source_object)); - g_assert (state != NULL); - g_assert (state->cursor != NULL); - - sysprof_capture_cursor_foreach (state->cursor, sysprof_mark_visualizer_row_add_rect, state); - - /* If any inferred rects are left incomplete, just drop them in as - * point events for now. - */ - g_hash_table_iter_init (&iter, state->inferred_rects); - while (g_hash_table_iter_next (&iter, &key, &value)) - { - InferredRect *rect = value; - - add_inferred_rect_point (state, rect); - } - g_hash_table_remove_all (state->inferred_rects); - - end_time = sysprof_capture_reader_get_end_time (sysprof_capture_cursor_get_reader (state->cursor)); - rectangles_set_end_time (state->rects, end_time); - g_task_return_pointer (task, g_steal_pointer (&state->rects), (GDestroyNotify)rectangles_free); -} - -static gboolean -sysprof_mark_visualizer_row_query_tooltip (GtkWidget *widget, - gint x, - gint y, - gboolean keyboard_mode, - GtkTooltip *tooltip) -{ - SysprofMarkVisualizerRow *self = (SysprofMarkVisualizerRow *)widget; - SysprofMarkVisualizerRowPrivate *priv = sysprof_mark_visualizer_row_get_instance_private (self); - - g_assert (SYSPROF_IS_MARK_VISUALIZER_ROW (self)); - - if (priv->rectangles == NULL) - return FALSE; - - return rectangles_query_tooltip (priv->rectangles, tooltip, priv->group, x, y); -} - -static gboolean -sysprof_mark_visualizer_row_draw (GtkWidget *widget, - cairo_t *cr) -{ - SysprofMarkVisualizerRow *self = (SysprofMarkVisualizerRow *)widget; - SysprofMarkVisualizerRowPrivate *priv = sysprof_mark_visualizer_row_get_instance_private (self); - GtkStyleContext *style_context; - GtkStateFlags flags; - GdkRGBA foreground; - GtkAllocation alloc; - gboolean ret; - - g_assert (SYSPROF_IS_MARK_VISUALIZER_ROW (widget)); - g_assert (cr != NULL); - - gtk_widget_get_allocation (widget, &alloc); - - ret = GTK_WIDGET_CLASS (sysprof_mark_visualizer_row_parent_class)->draw (widget, cr); - - if (priv->rectangles == NULL) - return ret; - - style_context = gtk_widget_get_style_context (widget); - flags = gtk_widget_get_state_flags (widget); - gtk_style_context_get_color (style_context, flags, &foreground); - - rectangles_draw (priv->rectangles, GTK_WIDGET (self), cr); - - return ret; -} - -static void -data_load_cb (GObject *object, - GAsyncResult *result, - gpointer user_data) -{ - SysprofMarkVisualizerRow *self = (SysprofMarkVisualizerRow *)object; - SysprofMarkVisualizerRowPrivate *priv = sysprof_mark_visualizer_row_get_instance_private (self); - - g_assert (SYSPROF_IS_MARK_VISUALIZER_ROW (self)); - g_assert (G_IS_TASK (result)); - - g_clear_pointer (&priv->rectangles, rectangles_free); - priv->rectangles = g_task_propagate_pointer (G_TASK (result), NULL); - gtk_widget_queue_draw (GTK_WIDGET (self)); -} - -static void -sysprof_mark_visualizer_row_reload (SysprofMarkVisualizerRow *self) -{ - SysprofMarkVisualizerRowPrivate *priv = sysprof_mark_visualizer_row_get_instance_private (self); - g_autoptr(SysprofCaptureCursor) cursor = NULL; - g_autoptr(GTask) task = NULL; - SysprofCaptureCondition *condition; - BuildState *state; - - g_assert (SYSPROF_IS_MARK_VISUALIZER_ROW (self)); - - g_clear_pointer (&priv->rectangles, rectangles_free); - - condition = sysprof_capture_condition_new_where_type_in (1, (SysprofCaptureFrameType[]) { SYSPROF_CAPTURE_FRAME_MARK }); - cursor = sysprof_capture_cursor_new (priv->reader); - sysprof_capture_cursor_add_condition (cursor, g_steal_pointer (&condition)); - - state = g_slice_new0 (BuildState); - state->inferred_rects = g_hash_table_new_full (g_str_hash, g_str_equal, - NULL, - (GDestroyNotify)free_inferred_rect); - state->group = g_strdup (priv->group); - state->cursor = g_steal_pointer (&cursor); - state->rects = rectangles_new (sysprof_capture_reader_get_start_time (priv->reader), - sysprof_capture_reader_get_end_time (priv->reader)); - - task = g_task_new (self, NULL, data_load_cb, NULL); - g_task_set_task_data (task, state, (GDestroyNotify)build_state_free); - g_task_run_in_thread (task, sysprof_mark_visualizer_row_worker); -} - -static void -sysprof_mark_visualizer_row_set_reader (SysprofVisualizerRow *row, - SysprofCaptureReader *reader) -{ - SysprofMarkVisualizerRow *self = (SysprofMarkVisualizerRow *)row; - SysprofMarkVisualizerRowPrivate *priv = sysprof_mark_visualizer_row_get_instance_private (self); - - g_assert (SYSPROF_IS_MARK_VISUALIZER_ROW (self)); - - if (reader != priv->reader) - { - g_clear_pointer (&priv->reader, sysprof_capture_reader_unref); - if (reader != NULL) - priv->reader = sysprof_capture_reader_ref (reader); - sysprof_mark_visualizer_row_reload (self); - } -} - -static void -sysprof_mark_visualizer_row_finalize (GObject *object) -{ - SysprofMarkVisualizerRow *self = (SysprofMarkVisualizerRow *)object; - SysprofMarkVisualizerRowPrivate *priv = sysprof_mark_visualizer_row_get_instance_private (self); - - g_clear_pointer (&priv->group, g_free); - g_clear_pointer (&priv->rectangles, rectangles_free); - - G_OBJECT_CLASS (sysprof_mark_visualizer_row_parent_class)->finalize (object); -} - -static void -sysprof_mark_visualizer_row_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofMarkVisualizerRow *self = SYSPROF_MARK_VISUALIZER_ROW (object); - SysprofMarkVisualizerRowPrivate *priv = sysprof_mark_visualizer_row_get_instance_private (self); - - switch (prop_id) - { - case PROP_GROUP: - g_value_set_string (value, sysprof_mark_visualizer_row_get_group (self)); - break; - - case PROP_TITLE: - g_value_set_string (value, gtk_label_get_label (priv->label)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_mark_visualizer_row_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofMarkVisualizerRow *self = SYSPROF_MARK_VISUALIZER_ROW (object); - SysprofMarkVisualizerRowPrivate *priv = sysprof_mark_visualizer_row_get_instance_private (self); - - switch (prop_id) - { - case PROP_GROUP: - sysprof_mark_visualizer_row_set_group (self, g_value_get_string (value)); - break; - - case PROP_TITLE: - gtk_label_set_label (priv->label, g_value_get_string (value)); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_mark_visualizer_row_class_init (SysprofMarkVisualizerRowClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - SysprofVisualizerRowClass *visualizer_class = SYSPROF_VISUALIZER_ROW_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - object_class->finalize = sysprof_mark_visualizer_row_finalize; - object_class->get_property = sysprof_mark_visualizer_row_get_property; - object_class->set_property = sysprof_mark_visualizer_row_set_property; - - widget_class->draw = sysprof_mark_visualizer_row_draw; - widget_class->query_tooltip = sysprof_mark_visualizer_row_query_tooltip; - - visualizer_class->set_reader = sysprof_mark_visualizer_row_set_reader; - - properties [PROP_GROUP] = - g_param_spec_string ("group", - "Group", - "The group of the row", - NULL, - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - properties [PROP_TITLE] = - g_param_spec_string ("title", - "Title", - "The title of the row", - NULL, - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); -} - -static void -sysprof_mark_visualizer_row_init (SysprofMarkVisualizerRow *self) -{ - SysprofMarkVisualizerRowPrivate *priv = sysprof_mark_visualizer_row_get_instance_private (self); - PangoAttrList *attrs = pango_attr_list_new (); - - gtk_widget_set_has_tooltip (GTK_WIDGET (self), TRUE); - - pango_attr_list_insert (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL * PANGO_SCALE_SMALL)); - - priv->label = g_object_new (GTK_TYPE_LABEL, - "attributes", attrs, - "visible", TRUE, - "xalign", 0.0f, - "yalign", 0.0f, - NULL); - gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (priv->label)); - - pango_attr_list_unref (attrs); -} - -GtkWidget * -sysprof_mark_visualizer_row_new (void) -{ - return g_object_new (SYSPROF_TYPE_MARK_VISUALIZER_ROW, NULL); -} - -const gchar * -sysprof_mark_visualizer_row_get_group (SysprofMarkVisualizerRow *self) -{ - SysprofMarkVisualizerRowPrivate *priv = sysprof_mark_visualizer_row_get_instance_private (self); - - g_return_val_if_fail (SYSPROF_IS_MARK_VISUALIZER_ROW (self), NULL); - - return priv->group; -} - -void -sysprof_mark_visualizer_row_set_group (SysprofMarkVisualizerRow *self, - const gchar *group) -{ - SysprofMarkVisualizerRowPrivate *priv = sysprof_mark_visualizer_row_get_instance_private (self); - - g_return_if_fail (SYSPROF_IS_MARK_VISUALIZER_ROW (self)); - - if (g_strcmp0 (priv->group, group) != 0) - { - g_free (priv->group); - priv->group = g_strdup (group); - g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_GROUP]); - } -} diff --git a/src/libsysprof-ui/sysprof-mark-visualizer-row.h b/src/libsysprof-ui/sysprof-mark-visualizer-row.h deleted file mode 100644 index f560e63a..00000000 --- a/src/libsysprof-ui/sysprof-mark-visualizer-row.h +++ /dev/null @@ -1,53 +0,0 @@ -/* sysprof-mark-visualizer-row.h - * - * Copyright 2018-2019 Christian Hergert - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -#pragma once - -#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION) -# error "Only can be included directly." -#endif - -#include "sysprof-visualizer-row.h" -#include "sysprof-version-macros.h" - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_MARK_VISUALIZER_ROW (sysprof_mark_visualizer_row_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_DERIVABLE_TYPE (SysprofMarkVisualizerRow, sysprof_mark_visualizer_row, SYSPROF, MARK_VISUALIZER_ROW, SysprofVisualizerRow) - -struct _SysprofMarkVisualizerRowClass -{ - SysprofVisualizerRowClass parent_class; - - /*< private >*/ - gpointer _reserved[16]; -}; - -SYSPROF_AVAILABLE_IN_ALL -GtkWidget *sysprof_mark_visualizer_row_new (void); -SYSPROF_AVAILABLE_IN_ALL -const gchar *sysprof_mark_visualizer_row_get_group (SysprofMarkVisualizerRow *self); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_mark_visualizer_row_set_group (SysprofMarkVisualizerRow *self, - const gchar *group); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-mark-visualizer.c b/src/libsysprof-ui/sysprof-mark-visualizer.c new file mode 100644 index 00000000..216cf62a --- /dev/null +++ b/src/libsysprof-ui/sysprof-mark-visualizer.c @@ -0,0 +1,287 @@ +/* sysprof-mark-visualizer.c + * + * Copyright 2018-2019 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#define G_LOG_DOMAIN "sysprof-mark-visualizer" + +#include "config.h" + +#include "sysprof-mark-visualizer.h" + +#define RECT_HEIGHT (4) +#define RECT_MIN_WIDTH (3) +#define RECT_OVERLAP (-1) + +struct _SysprofMarkVisualizer +{ + SysprofVisualizer parent_instance; + GHashTable *spans_by_group; + GHashTable *rgba_by_group; + GHashTable *rgba_by_kind; + GHashTable *row_by_kind; + guint x_is_dirty : 1; +}; + +G_DEFINE_TYPE (SysprofMarkVisualizer, sysprof_mark_visualizer, SYSPROF_TYPE_VISUALIZER) + +static void +reset_positions (SysprofMarkVisualizer *self) +{ + g_assert (SYSPROF_IS_MARK_VISUALIZER (self)); + + self->x_is_dirty = TRUE; + gtk_widget_queue_draw (GTK_WIDGET (self)); +} + +SysprofVisualizer * +sysprof_mark_visualizer_new (GHashTable *groups) +{ + SysprofMarkVisualizer *self; + guint n_items; + gint height; + + g_return_val_if_fail (groups != NULL, NULL); + + self = g_object_new (SYSPROF_TYPE_MARK_VISUALIZER, NULL); + self->spans_by_group = g_hash_table_ref (groups); + + reset_positions (self); + + n_items = g_hash_table_size (groups); + height = MAX (35, n_items * (RECT_HEIGHT - RECT_OVERLAP)); + gtk_widget_set_size_request (GTK_WIDGET (self), -1, height); + + return SYSPROF_VISUALIZER (g_steal_pointer (&self)); +} + +static gboolean +sysprof_mark_visualizer_draw (GtkWidget *widget, + cairo_t *cr) +{ + SysprofMarkVisualizer *self = (SysprofMarkVisualizer *)widget; + SysprofVisualizer *vis = (SysprofVisualizer *)widget; + GHashTableIter iter; + GtkAllocation alloc; + gpointer k, v; + gboolean ret; + gint n_groups = 0; + gint y = 0; + + g_assert (SYSPROF_IS_MARK_VISUALIZER (self)); + g_assert (cr != NULL); + + ret = GTK_WIDGET_CLASS (sysprof_mark_visualizer_parent_class)->draw (widget, cr); + if (self->spans_by_group == NULL) + return ret; + + gtk_widget_get_allocation (widget, &alloc); + + /* Pre-calculate all time slots so we can join later */ + if (self->x_is_dirty) + { + g_hash_table_iter_init (&iter, self->spans_by_group); + while (g_hash_table_iter_next (&iter, &k, &v)) + { + const GArray *spans = v; + + for (guint i = 0; i < spans->len; i++) + { + SysprofMarkTimeSpan *span = &g_array_index (spans, SysprofMarkTimeSpan, i); + + span->x = sysprof_visualizer_get_x_for_time (vis, span->begin); + span->x2 = sysprof_visualizer_get_x_for_time (vis, span->end); + } + } + + self->x_is_dirty = FALSE; + } + + n_groups = g_hash_table_size (self->spans_by_group); + + g_hash_table_iter_init (&iter, self->spans_by_group); + while (g_hash_table_iter_next (&iter, &k, &v)) + { + static const GdkRGBA black = {0, 0, 0, 1}; + SysprofMarkTimeSpan *span; + const gchar *group = k; + const GArray *spans = v; + const GdkRGBA *rgba; + const GdkRGBA *kindrgba; + const GdkRGBA *grouprgba; + + if ((grouprgba = g_hash_table_lookup (self->rgba_by_group, group))) + { + rgba = grouprgba; + gdk_cairo_set_source_rgba (cr, rgba); + } + + for (guint i = 0; i < spans->len; i++) + { + gint x1, x2; + + span = &g_array_index (spans, SysprofMarkTimeSpan, i); + + if (n_groups == 1) + { + rgba = &black; + if ((kindrgba = g_hash_table_lookup (self->rgba_by_kind, GUINT_TO_POINTER (span->kind)))) + rgba = kindrgba; + else if ((grouprgba = g_hash_table_lookup (self->rgba_by_group, group))) + rgba = grouprgba; + gdk_cairo_set_source_rgba (cr, rgba); + } + + x1 = span->x; + x2 = x1 + RECT_MIN_WIDTH; + + if (span->x2 > x2) + x2 = span->x2; + + /* If we are limited to one group, we might need to get the row + * height for the kind of span this is. + */ + if (n_groups == 1) + { + gint row = GPOINTER_TO_INT (g_hash_table_lookup (self->row_by_kind, GUINT_TO_POINTER (span->kind))); + y = row * (RECT_HEIGHT - RECT_OVERLAP); + } + + for (guint j = i + 1; j < spans->len; j++) + { + const SysprofMarkTimeSpan *next = &g_array_index (spans, SysprofMarkTimeSpan, j); + + /* Don't join this if we are about to draw a different kind */ + if (n_groups == 1 && next->kind != span->kind) + break; + + if (next->x <= x2) + { + x2 = MAX (x2, next->x2); + i++; + continue; + } + + break; + } + + cairo_rectangle (cr, x1, y, x2 - x1, RECT_HEIGHT); + + if (n_groups == 1) + cairo_fill (cr); + } + + if (n_groups > 1) + cairo_fill (cr); + + y += RECT_HEIGHT + RECT_OVERLAP; + } + + return ret; +} + +static void +sysprof_mark_visualizer_size_allocate (GtkWidget *widget, + GtkAllocation *alloc) +{ + SysprofMarkVisualizer *self = (SysprofMarkVisualizer *)widget; + + g_assert (SYSPROF_IS_MARK_VISUALIZER (self)); + g_assert (alloc != NULL); + + GTK_WIDGET_CLASS (sysprof_mark_visualizer_parent_class)->size_allocate (widget, alloc); + + reset_positions (self); +} + +static void +sysprof_mark_visualizer_finalize (GObject *object) +{ + SysprofMarkVisualizer *self = (SysprofMarkVisualizer *)object; + + g_clear_pointer (&self->spans_by_group, g_hash_table_unref); + g_clear_pointer (&self->rgba_by_group, g_hash_table_unref); + g_clear_pointer (&self->rgba_by_kind, g_hash_table_unref); + g_clear_pointer (&self->row_by_kind, g_hash_table_unref); + + G_OBJECT_CLASS (sysprof_mark_visualizer_parent_class)->finalize (object); +} + +static void +sysprof_mark_visualizer_class_init (SysprofMarkVisualizerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->finalize = sysprof_mark_visualizer_finalize; + + widget_class->draw = sysprof_mark_visualizer_draw; + widget_class->size_allocate = sysprof_mark_visualizer_size_allocate; +} + +static void +sysprof_mark_visualizer_init (SysprofMarkVisualizer *self) +{ + self->rgba_by_kind = g_hash_table_new_full (NULL, NULL, NULL, g_free); + self->row_by_kind = g_hash_table_new (NULL, NULL); + self->rgba_by_group = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); +} + +void +sysprof_mark_visualizer_set_group_rgba (SysprofMarkVisualizer *self, + const gchar *group, + const GdkRGBA *rgba) +{ + g_return_if_fail (SYSPROF_IS_MARK_VISUALIZER (self)); + g_return_if_fail (group != NULL); + + g_hash_table_insert (self->rgba_by_group, + g_strdup (group), + g_memdup (rgba, sizeof *rgba)); +} + +void +sysprof_mark_visualizer_set_kind_rgba (SysprofMarkVisualizer *self, + GHashTable *rgba_by_kind) +{ + g_return_if_fail (SYSPROF_IS_MARK_VISUALIZER (self)); + + if (rgba_by_kind != self->rgba_by_kind) + { + g_hash_table_remove_all (self->row_by_kind); + + g_clear_pointer (&self->rgba_by_kind, g_hash_table_unref); + + if (rgba_by_kind) + { + GHashTableIter iter; + guint row = 0; + gpointer k; + + self->rgba_by_kind = g_hash_table_ref (rgba_by_kind); + + g_hash_table_iter_init (&iter, rgba_by_kind); + while (g_hash_table_iter_next (&iter, &k, NULL)) + g_hash_table_insert (self->row_by_kind, k, GUINT_TO_POINTER (row++)); + + gtk_widget_set_size_request (GTK_WIDGET (self), + -1, + MAX (35, row * (RECT_HEIGHT - RECT_OVERLAP))); + } + } +} diff --git a/src/libsysprof-ui/sysprof-mark-visualizer.h b/src/libsysprof-ui/sysprof-mark-visualizer.h new file mode 100644 index 00000000..36327e13 --- /dev/null +++ b/src/libsysprof-ui/sysprof-mark-visualizer.h @@ -0,0 +1,47 @@ +/* sysprof-mark-visualizer.h + * + * Copyright 2018-2019 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-visualizer.h" + +G_BEGIN_DECLS + +typedef struct +{ + gint64 begin; + gint64 end; + guint kind; + gint x; + gint x2; +} SysprofMarkTimeSpan; + +#define SYSPROF_TYPE_MARK_VISUALIZER (sysprof_mark_visualizer_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofMarkVisualizer, sysprof_mark_visualizer, SYSPROF, MARK_VISUALIZER, SysprofVisualizer) + +SysprofVisualizer *sysprof_mark_visualizer_new (GHashTable *groups); +void sysprof_mark_visualizer_set_group_rgba (SysprofMarkVisualizer *self, + const gchar *group, + const GdkRGBA *rgba); +void sysprof_mark_visualizer_set_kind_rgba (SysprofMarkVisualizer *self, + GHashTable *rgba_by_kind); + +G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-marks-aid.c b/src/libsysprof-ui/sysprof-marks-aid.c new file mode 100644 index 00000000..a8f79b48 --- /dev/null +++ b/src/libsysprof-ui/sysprof-marks-aid.c @@ -0,0 +1,319 @@ +/* sysprof-marks-aid.c + * + * Copyright 2019 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#define G_LOG_DOMAIN "sysprof-marks-aid" + +#include "config.h" + +#include + +#include "sysprof-color-cycle.h" +#include "sysprof-marks-aid.h" +#include "sysprof-marks-page.h" +#include "sysprof-mark-visualizer.h" + +struct _SysprofMarksAid +{ + SysprofAid parent_instance; +}; + +typedef struct +{ + SysprofDisplay *display; + SysprofCaptureCursor *cursor; + GHashTable *categories; + GHashTable *kinds; + guint last_kind; + guint has_marks : 1; +} Present; + +G_DEFINE_TYPE (SysprofMarksAid, sysprof_marks_aid, SYSPROF_TYPE_AID) + +static void +present_free (gpointer data) +{ + Present *p = data; + + g_clear_pointer (&p->categories, g_hash_table_unref); + g_clear_pointer (&p->kinds, g_hash_table_unref); + g_clear_pointer (&p->cursor, sysprof_capture_cursor_unref); + g_clear_object (&p->display); + g_slice_free (Present, p); +} + +static void +on_group_activated_cb (SysprofVisualizerGroup *group, + SysprofPage *page) +{ + SysprofDisplay *display; + + g_assert (SYSPROF_IS_VISUALIZER_GROUP (group)); + g_assert (SYSPROF_IS_PAGE (page)); + + display = SYSPROF_DISPLAY (gtk_widget_get_ancestor (GTK_WIDGET (page), SYSPROF_TYPE_DISPLAY)); + sysprof_display_set_visible_page (display, page); +} + +/** + * sysprof_marks_aid_new: + * + * Create a new #SysprofMarksAid. + * + * Returns: (transfer full): a newly created #SysprofMarksAid + */ +SysprofAid * +sysprof_marks_aid_new (void) +{ + return g_object_new (SYSPROF_TYPE_MARKS_AID, NULL); +} + +static gboolean +find_marks_cb (const SysprofCaptureFrame *frame, + gpointer user_data) +{ + Present *p = user_data; + + g_assert (frame != NULL); + g_assert (p != NULL); + + if (frame->type == SYSPROF_CAPTURE_FRAME_MARK) + { + const SysprofCaptureMark *mark = (const SysprofCaptureMark *)frame; + SysprofMarkTimeSpan span = { frame->time, frame->time + mark->duration }; + gchar joined[64]; + gpointer kptr; + GArray *items; + + p->has_marks = TRUE; + + if G_UNLIKELY (!(items = g_hash_table_lookup (p->categories, mark->group))) + { + items = g_array_new (FALSE, FALSE, sizeof (SysprofMarkTimeSpan)); + g_hash_table_insert (p->categories, g_strdup (mark->group), items); + } + + g_snprintf (joined, sizeof joined, "%s:%s", mark->group, mark->name); + + if G_UNLIKELY (!(kptr = g_hash_table_lookup (p->kinds, joined))) + { + p->last_kind++; + kptr = GINT_TO_POINTER (p->last_kind); + g_hash_table_insert (p->kinds, g_strdup (joined), kptr); + } + + span.kind = GPOINTER_TO_INT (kptr); + + g_array_append_val (items, span); + } + + return TRUE; +} + +static gint +compare_span (const SysprofMarkTimeSpan *a, + const SysprofMarkTimeSpan *b) +{ + if (a->kind < b->kind) + return -1; + + if (b->kind < a->kind) + return 1; + + if (a->begin < b->begin) + return -1; + + if (b->begin < a->begin) + return 1; + + if (b->end > a->end) + return -1; + + return 0; +} + +static void +sysprof_marks_aid_present_worker (GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + Present *p = task_data; + GHashTableIter iter; + gpointer k, v; + + g_assert (G_IS_TASK (task)); + g_assert (p != NULL); + g_assert (SYSPROF_IS_DISPLAY (p->display)); + g_assert (p->cursor != NULL); + g_assert (SYSPROF_IS_MARKS_AID (source_object)); + + sysprof_capture_cursor_foreach (p->cursor, find_marks_cb, p); + + g_hash_table_iter_init (&iter, p->categories); + while (g_hash_table_iter_next (&iter, &k, &v)) + { + GArray *spans = v; + + g_array_sort (spans, (GCompareFunc)compare_span); + } + + g_task_return_boolean (task, TRUE); +} + +static void +sysprof_marks_aid_present_async (SysprofAid *aid, + SysprofCaptureReader *reader, + SysprofDisplay *display, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + static const SysprofCaptureFrameType marks[] = { + SYSPROF_CAPTURE_FRAME_MARK, + }; + SysprofMarksAid *self = (SysprofMarksAid *)aid; + g_autoptr(GTask) task = NULL; + Present p = {0}; + + g_assert (SYSPROF_IS_MARKS_AID (self)); + + p.display = g_object_ref (display); + p.categories = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, + (GDestroyNotify) g_array_unref); + p.kinds = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + p.cursor = sysprof_capture_cursor_new (reader); + sysprof_capture_cursor_add_condition (p.cursor, + sysprof_capture_condition_new_where_type_in (1, marks)); + + task = g_task_new (self, cancellable, callback, user_data); + g_task_set_source_tag (task, sysprof_marks_aid_present_async); + g_task_set_task_data (task, + g_slice_dup (Present, &p), + present_free); + g_task_run_in_thread (task, sysprof_marks_aid_present_worker); +} + +static gboolean +sysprof_marks_aid_present_finish (SysprofAid *aid, + GAsyncResult *result, + GError **error) +{ + Present *p; + + g_assert (SYSPROF_IS_MARKS_AID (aid)); + g_assert (G_IS_TASK (result)); + + p = g_task_get_task_data (G_TASK (result)); + + if (p->has_marks) + { + g_autoptr(SysprofColorCycle) cycle = sysprof_color_cycle_new (); + SysprofVisualizerGroup *group; + SysprofVisualizer *marks; + SysprofPage *page; + GHashTableIter iter; + gpointer k, v; + + group = g_object_new (SYSPROF_TYPE_VISUALIZER_GROUP, + "can-focus", TRUE, + "has-page", TRUE, + "title", _("Timings"), + "visible", TRUE, + NULL); + + marks = sysprof_mark_visualizer_new (p->categories); + sysprof_visualizer_set_title (marks, _("Timings")); + gtk_widget_show (GTK_WIDGET (marks)); + + g_hash_table_iter_init (&iter, p->categories); + while (g_hash_table_iter_next (&iter, &k, &v)) + { + g_autoptr(GHashTable) seen = g_hash_table_new_full (NULL, NULL, NULL, g_free); + g_autoptr(GHashTable) scoped = NULL; + SysprofVisualizer *scoped_vis; + GArray *spans = v; + const gchar *name = k; + GdkRGBA rgba; + GdkRGBA kind_rgba; + gdouble ratio; + + sysprof_color_cycle_next (cycle, &rgba); + sysprof_mark_visualizer_set_group_rgba (SYSPROF_MARK_VISUALIZER (marks), name, &rgba); + + /* Now make a scoped row just for this group */ + scoped = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, + (GDestroyNotify)g_array_unref); + g_hash_table_insert (scoped, g_strdup (name), g_array_ref (spans)); + + scoped_vis = sysprof_mark_visualizer_new (scoped); + sysprof_visualizer_set_title (scoped_vis, name); + sysprof_mark_visualizer_set_group_rgba (SYSPROF_MARK_VISUALIZER (scoped_vis), name, &rgba); + sysprof_visualizer_group_insert (group, scoped_vis, -1, TRUE); + + ratio = .4 / p->last_kind; + + for (guint i = 0; i < spans->len; i++) + { + const SysprofMarkTimeSpan *span = &g_array_index (spans, SysprofMarkTimeSpan, i); + + if (!g_hash_table_contains (seen, GUINT_TO_POINTER (span->kind))) + { + dzl_rgba_shade (&rgba, &kind_rgba, 1 + (ratio * span->kind)); + g_hash_table_insert (seen, + GUINT_TO_POINTER (span->kind), + g_memdup (&kind_rgba, sizeof kind_rgba)); + } + } + + sysprof_mark_visualizer_set_kind_rgba (SYSPROF_MARK_VISUALIZER (scoped_vis), seen); + } + + page = g_object_new (SYSPROF_TYPE_MARKS_PAGE, + "zoom-manager", sysprof_display_get_zoom_manager (p->display), + "visible", TRUE, + NULL); + + g_signal_connect_object (group, + "group-activated", + G_CALLBACK (on_group_activated_cb), + page, + 0); + + sysprof_visualizer_group_insert (group, marks, 0, FALSE); + sysprof_display_add_group (p->display, group); + sysprof_display_add_page (p->display, page); + } + + return g_task_propagate_boolean (G_TASK (result), error); +} + +static void +sysprof_marks_aid_class_init (SysprofMarksAidClass *klass) +{ + SysprofAidClass *aid_class = SYSPROF_AID_CLASS (klass); + + aid_class->present_async = sysprof_marks_aid_present_async; + aid_class->present_finish = sysprof_marks_aid_present_finish; +} + +static void +sysprof_marks_aid_init (SysprofMarksAid *self) +{ +} diff --git a/src/libsysprof-ui/sysprof-marks-aid.h b/src/libsysprof-ui/sysprof-marks-aid.h new file mode 100644 index 00000000..f201f3aa --- /dev/null +++ b/src/libsysprof-ui/sysprof-marks-aid.h @@ -0,0 +1,33 @@ +/* sysprof-marks-aid.h + * + * Copyright 2019 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "sysprof-aid.h" + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_MARKS_AID (sysprof_marks_aid_get_type()) + +G_DECLARE_FINAL_TYPE (SysprofMarksAid, sysprof_marks_aid, SYSPROF, MARKS_AID, SysprofAid) + +SysprofAid *sysprof_marks_aid_new (void); + +G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-marks-view.c b/src/libsysprof-ui/sysprof-marks-page.c similarity index 64% rename from src/libsysprof-ui/sysprof-marks-view.c rename to src/libsysprof-ui/sysprof-marks-page.c index c09d71dc..3c4ce2fb 100644 --- a/src/libsysprof-ui/sysprof-marks-view.c +++ b/src/libsysprof-ui/sysprof-marks-page.c @@ -1,4 +1,4 @@ -/* sysprof-marks-view.c +/* sysprof-marks-page.c * * Copyright 2019 Christian Hergert * @@ -18,13 +18,13 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -#define G_LOG_DOMAIN "sysprof-marks-view" +#define G_LOG_DOMAIN "sysprof-marks-page" #include "config.h" #include "sysprof-cell-renderer-duration.h" #include "sysprof-marks-model.h" -#include "sysprof-marks-view.h" +#include "sysprof-marks-page.h" #include "sysprof-ui-private.h" #include "sysprof-zoom-manager.h" @@ -42,10 +42,16 @@ typedef struct /* Template objects */ GtkScrolledWindow *scroller; GtkTreeView *tree_view; + GtkBox *details_box; GtkTreeViewColumn *duration_column; SysprofCellRendererDuration *duration_cell; GtkStack *stack; -} SysprofMarksViewPrivate; + GtkLabel *group; + GtkLabel *mark; + GtkLabel *time; + GtkLabel *duration; + GtkTextView *message; +} SysprofMarksPagePrivate; enum { PROP_0, @@ -56,17 +62,17 @@ enum { static GParamSpec *properties [N_PROPS]; -G_DEFINE_TYPE_WITH_PRIVATE (SysprofMarksView, sysprof_marks_view, GTK_TYPE_BIN) +G_DEFINE_TYPE_WITH_PRIVATE (SysprofMarksPage, sysprof_marks_page, SYSPROF_TYPE_PAGE) static gboolean -sysprof_marks_view_tree_view_key_press_event_cb (SysprofMarksView *self, +sysprof_marks_page_tree_view_key_press_event_cb (SysprofMarksPage *self, const GdkEventKey *key, GtkTreeView *tree_view) { - SysprofMarksViewPrivate *priv = sysprof_marks_view_get_instance_private (self); + SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self); gint dir = 0; - g_assert (SYSPROF_MARKS_VIEW (self)); + g_assert (SYSPROF_MARKS_PAGE (self)); g_assert (key != NULL); if (key->state == 0) @@ -127,21 +133,29 @@ get_first_selected (GtkTreeSelection *selection, } static void -sysprof_marks_view_selection_changed_cb (SysprofMarksView *self, +sysprof_marks_page_selection_changed_cb (SysprofMarksPage *self, GtkTreeSelection *selection) { - SysprofMarksViewPrivate *priv = sysprof_marks_view_get_instance_private (self); + SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self); GtkTreeModel *model; GtkTreeIter iter; - g_assert (SYSPROF_IS_MARKS_VIEW (self)); + g_assert (SYSPROF_IS_MARKS_PAGE (self)); g_assert (GTK_IS_TREE_SELECTION (selection)); if (get_first_selected (selection, &model, &iter)) { + g_autofree gchar *group = NULL; + g_autofree gchar *name = NULL; + g_autofree gchar *duration_str = NULL; + g_autofree gchar *time_str = NULL; + g_autofree gchar *text = NULL; GtkAdjustment *adj; gdouble x; gint64 begin_time; + gint64 end_time; + gint64 duration; + gint64 otime; gdouble lower; gdouble upper; gdouble value; @@ -149,9 +163,26 @@ sysprof_marks_view_selection_changed_cb (SysprofMarksView *self, gint width; gtk_tree_model_get (model, &iter, + SYSPROF_MARKS_MODEL_COLUMN_GROUP, &group, + SYSPROF_MARKS_MODEL_COLUMN_NAME, &name, SYSPROF_MARKS_MODEL_COLUMN_BEGIN_TIME, &begin_time, + SYSPROF_MARKS_MODEL_COLUMN_END_TIME, &end_time, + SYSPROF_MARKS_MODEL_COLUMN_TEXT, &text, -1); + duration = end_time - begin_time; + duration_str = _sysprof_format_duration (duration); + + otime = begin_time - priv->capture_begin_time; + time_str = _sysprof_format_duration (otime); + + gtk_label_set_label (priv->group, group); + gtk_label_set_label (priv->mark, name); + gtk_label_set_label (priv->duration, duration_str); + gtk_label_set_label (priv->time, time_str); + + gtk_text_buffer_set_text (gtk_text_view_get_buffer (priv->message), text, -1); + adj = gtk_scrolled_window_get_hadjustment (priv->scroller); width = gtk_tree_view_column_get_width (priv->duration_column); x = sysprof_zoom_manager_get_offset_at_time (priv->zoom_manager, @@ -173,19 +204,19 @@ sysprof_marks_view_selection_changed_cb (SysprofMarksView *self, } static gboolean -sysprof_marks_view_tree_view_query_tooltip_cb (SysprofMarksView *self, +sysprof_marks_page_tree_view_query_tooltip_cb (SysprofMarksPage *self, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip, GtkTreeView *tree_view) { - SysprofMarksViewPrivate *priv = sysprof_marks_view_get_instance_private (self); + SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self); g_autoptr(GtkTreePath) path = NULL; GtkTreeViewColumn *column; gint cell_x, cell_y; - g_assert (SYSPROF_IS_MARKS_VIEW (self)); + g_assert (SYSPROF_IS_MARKS_PAGE (self)); g_assert (GTK_IS_TOOLTIP (tooltip)); g_assert (GTK_IS_TREE_VIEW (tree_view)); @@ -229,162 +260,22 @@ sysprof_marks_view_tree_view_query_tooltip_cb (SysprofMarksView *self, } static void -sysprof_marks_view_finalize (GObject *object) -{ - SysprofMarksView *self = (SysprofMarksView *)object; - SysprofMarksViewPrivate *priv = sysprof_marks_view_get_instance_private (self); - - g_clear_object (&priv->zoom_manager); - - G_OBJECT_CLASS (sysprof_marks_view_parent_class)->finalize (object); -} - -static void -sysprof_marks_view_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - SysprofMarksView *self = SYSPROF_MARKS_VIEW (object); - SysprofMarksViewPrivate *priv = sysprof_marks_view_get_instance_private (self); - - switch (prop_id) - { - case PROP_KIND: - g_value_set_enum (value, priv->kind); - break; - - case PROP_ZOOM_MANAGER: - g_value_set_object (value, priv->zoom_manager); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_marks_view_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - SysprofMarksView *self = SYSPROF_MARKS_VIEW (object); - SysprofMarksViewPrivate *priv = sysprof_marks_view_get_instance_private (self); - - switch (prop_id) - { - case PROP_KIND: - priv->kind = g_value_get_enum (value); - break; - - case PROP_ZOOM_MANAGER: - if (g_set_object (&priv->zoom_manager, g_value_get_object (value))) - { - g_object_set (priv->duration_cell, - "zoom-manager", priv->zoom_manager, - NULL); - if (priv->zoom_manager) - g_signal_connect_object (priv->zoom_manager, - "notify::zoom", - G_CALLBACK (gtk_tree_view_column_queue_resize), - priv->duration_column, - G_CONNECT_SWAPPED); - } - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -sysprof_marks_view_class_init (SysprofMarksViewClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - - object_class->finalize = sysprof_marks_view_finalize; - object_class->get_property = sysprof_marks_view_get_property; - object_class->set_property = sysprof_marks_view_set_property; - - gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-marks-view.ui"); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksView, scroller); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksView, tree_view); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksView, duration_cell); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksView, duration_column); - gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksView, stack); - - properties [PROP_KIND] = - g_param_spec_enum ("kind", NULL, NULL, - SYSPROF_TYPE_MARKS_MODEL_KIND, - SYSPROF_MARKS_MODEL_MARKS, - (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - - properties [PROP_ZOOM_MANAGER] = - g_param_spec_object ("zoom-manager", NULL, NULL, - SYSPROF_TYPE_ZOOM_MANAGER, - (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_properties (object_class, N_PROPS, properties); - - g_type_ensure (SYSPROF_TYPE_CELL_RENDERER_DURATION); -} - -static void -sysprof_marks_view_init (SysprofMarksView *self) -{ - SysprofMarksViewPrivate *priv = sysprof_marks_view_get_instance_private (self); - - priv->kind = SYSPROF_MARKS_MODEL_MARKS; - - gtk_widget_init_template (GTK_WIDGET (self)); - - gtk_tree_selection_set_mode (gtk_tree_view_get_selection (priv->tree_view), - GTK_SELECTION_MULTIPLE); - - g_signal_connect_object (priv->tree_view, - "key-press-event", - G_CALLBACK (sysprof_marks_view_tree_view_key_press_event_cb), - self, - G_CONNECT_SWAPPED); - - g_signal_connect_object (priv->tree_view, - "query-tooltip", - G_CALLBACK (sysprof_marks_view_tree_view_query_tooltip_cb), - self, - G_CONNECT_SWAPPED); - - g_signal_connect_object (gtk_tree_view_get_selection (priv->tree_view), - "changed", - G_CALLBACK (sysprof_marks_view_selection_changed_cb), - self, - G_CONNECT_SWAPPED); -} - -GtkWidget * -sysprof_marks_view_new (void) -{ - return g_object_new (SYSPROF_TYPE_MARKS_VIEW, NULL); -} - -static void -sysprof_marks_view_load_cb (GObject *object, +sysprof_marks_page_load_cb (GObject *object, GAsyncResult *result, gpointer user_data) { g_autoptr(SysprofMarksModel) model = NULL; g_autoptr(GError) error = NULL; g_autoptr(GTask) task = user_data; - SysprofMarksViewPrivate *priv; + SysprofMarksPagePrivate *priv; SysprofCaptureReader *reader; - SysprofMarksView *self; + SysprofMarksPage *self; g_assert (G_IS_ASYNC_RESULT (result)); g_assert (G_IS_TASK (task)); self = g_task_get_source_object (task); - priv = sysprof_marks_view_get_instance_private (self); + priv = sysprof_marks_page_get_instance_private (self); if (!(model = sysprof_marks_model_new_finish (result, &error))) { @@ -414,24 +305,26 @@ sysprof_marks_view_load_cb (GObject *object, g_task_return_boolean (task, TRUE); } -void -sysprof_marks_view_load_async (SysprofMarksView *self, - SysprofCaptureReader *reader, - SysprofSelection *selection, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +static void +sysprof_marks_page_load_async (SysprofPage *page, + SysprofCaptureReader *reader, + SysprofSelection *selection, + SysprofCaptureCondition *filter, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { - SysprofMarksViewPrivate *priv = sysprof_marks_view_get_instance_private (self); + SysprofMarksPage *self = (SysprofMarksPage *)page; + SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self); g_autoptr(GTask) task = NULL; - g_return_if_fail (SYSPROF_IS_MARKS_VIEW (self)); + g_return_if_fail (SYSPROF_IS_MARKS_PAGE (self)); g_return_if_fail (reader != NULL); g_return_if_fail (!selection || SYSPROF_IS_SELECTION (selection)); g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); task = g_task_new (self, cancellable, callback, user_data); - g_task_set_source_tag (task, sysprof_marks_view_load_async); + g_task_set_source_tag (task, sysprof_marks_page_load_async); g_task_set_task_data (task, sysprof_capture_reader_ref (reader), (GDestroyNotify) sysprof_capture_reader_unref); @@ -440,28 +333,218 @@ sysprof_marks_view_load_async (SysprofMarksView *self, priv->kind, selection, cancellable, - sysprof_marks_view_load_cb, + sysprof_marks_page_load_cb, g_steal_pointer (&task)); } -gboolean -sysprof_marks_view_load_finish (SysprofMarksView *self, - GAsyncResult *result, - GError **error) +static gboolean +sysprof_marks_page_load_finish (SysprofPage *page, + GAsyncResult *result, + GError **error) { - g_return_val_if_fail (SYSPROF_IS_MARKS_VIEW (self), FALSE); + g_return_val_if_fail (SYSPROF_IS_MARKS_PAGE (page), FALSE); g_return_val_if_fail (G_IS_TASK (result), FALSE); return g_task_propagate_boolean (G_TASK (result), error); } +static void +sysprof_marks_page_set_hadjustment (SysprofPage *page, + GtkAdjustment *hadjustment) +{ + SysprofMarksPage *self = (SysprofMarksPage *)page; + SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self); + + g_assert (SYSPROF_IS_MARKS_PAGE (self)); + g_assert (!hadjustment || GTK_IS_ADJUSTMENT (hadjustment)); + + gtk_scrolled_window_set_hadjustment (priv->scroller, hadjustment); +} + +static void +sysprof_marks_page_set_size_group (SysprofPage *page, + GtkSizeGroup *size_group) +{ + SysprofMarksPage *self = (SysprofMarksPage *)page; + SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self); + + g_assert (SYSPROF_IS_MARKS_PAGE (self)); + g_assert (GTK_IS_SIZE_GROUP (size_group)); + + gtk_size_group_add_widget (size_group, GTK_WIDGET (priv->details_box)); +} + +static void +sysprof_marks_page_finalize (GObject *object) +{ + SysprofMarksPage *self = (SysprofMarksPage *)object; + SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self); + + g_clear_object (&priv->zoom_manager); + + G_OBJECT_CLASS (sysprof_marks_page_parent_class)->finalize (object); +} + +static void +sysprof_marks_page_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofMarksPage *self = SYSPROF_MARKS_PAGE (object); + SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self); + + switch (prop_id) + { + case PROP_KIND: + g_value_set_enum (value, priv->kind); + break; + + case PROP_ZOOM_MANAGER: + g_value_set_object (value, priv->zoom_manager); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_marks_page_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofMarksPage *self = SYSPROF_MARKS_PAGE (object); + SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self); + + switch (prop_id) + { + case PROP_KIND: + priv->kind = g_value_get_enum (value); + break; + + case PROP_ZOOM_MANAGER: + if (g_set_object (&priv->zoom_manager, g_value_get_object (value))) + { + g_object_set (priv->duration_cell, + "zoom-manager", priv->zoom_manager, + NULL); + if (priv->zoom_manager) + g_signal_connect_object (priv->zoom_manager, + "notify::zoom", + G_CALLBACK (gtk_tree_view_column_queue_resize), + priv->duration_column, + G_CONNECT_SWAPPED); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_marks_page_class_init (SysprofMarksPageClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + SysprofPageClass *page_class = SYSPROF_PAGE_CLASS (klass); + + object_class->finalize = sysprof_marks_page_finalize; + object_class->get_property = sysprof_marks_page_get_property; + object_class->set_property = sysprof_marks_page_set_property; + + page_class->load_async = sysprof_marks_page_load_async; + page_class->load_finish = sysprof_marks_page_load_finish; + page_class->set_hadjustment = sysprof_marks_page_set_hadjustment; + page_class->set_size_group = sysprof_marks_page_set_size_group; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/sysprof/ui/sysprof-marks-page.ui"); + gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, details_box); + gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, duration_cell); + gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, duration_column); + gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, scroller); + gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, stack); + gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, tree_view); + gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, group); + gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, mark); + gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, duration); + gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, time); + gtk_widget_class_bind_template_child_private (widget_class, SysprofMarksPage, message); + + properties [PROP_KIND] = + g_param_spec_enum ("kind", NULL, NULL, + SYSPROF_TYPE_MARKS_MODEL_KIND, + SYSPROF_MARKS_MODEL_MARKS, + (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + properties [PROP_ZOOM_MANAGER] = + g_param_spec_object ("zoom-manager", NULL, NULL, + SYSPROF_TYPE_ZOOM_MANAGER, + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); + + g_type_ensure (SYSPROF_TYPE_CELL_RENDERER_DURATION); +} + +static void +sysprof_marks_page_init (SysprofMarksPage *self) +{ + SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self); + + priv->kind = SYSPROF_MARKS_MODEL_MARKS; + + gtk_widget_init_template (GTK_WIDGET (self)); + + gtk_tree_selection_set_mode (gtk_tree_view_get_selection (priv->tree_view), + GTK_SELECTION_MULTIPLE); + + g_signal_connect_object (priv->tree_view, + "key-press-event", + G_CALLBACK (sysprof_marks_page_tree_view_key_press_event_cb), + self, + G_CONNECT_SWAPPED); + + g_signal_connect_object (priv->tree_view, + "query-tooltip", + G_CALLBACK (sysprof_marks_page_tree_view_query_tooltip_cb), + self, + G_CONNECT_SWAPPED); + + g_signal_connect_object (gtk_tree_view_get_selection (priv->tree_view), + "changed", + G_CALLBACK (sysprof_marks_page_selection_changed_cb), + self, + G_CONNECT_SWAPPED); +} + +GtkWidget * +sysprof_marks_page_new (SysprofZoomManager *zoom_manager, + SysprofMarksModelKind kind) +{ + SysprofMarksPage *self; + SysprofMarksPagePrivate *priv; + + g_return_val_if_fail (SYSPROF_IS_ZOOM_MANAGER (zoom_manager), NULL); + + self = g_object_new (SYSPROF_TYPE_MARKS_PAGE, + "zoom-manager", zoom_manager, + NULL); + priv = sysprof_marks_page_get_instance_private (self); + priv->kind = kind; + + return GTK_WIDGET (self); +} + void -_sysprof_marks_view_set_hadjustment (SysprofMarksView *self, +_sysprof_marks_page_set_hadjustment (SysprofMarksPage *self, GtkAdjustment *hadjustment) { - SysprofMarksViewPrivate *priv = sysprof_marks_view_get_instance_private (self); + SysprofMarksPagePrivate *priv = sysprof_marks_page_get_instance_private (self); - g_return_if_fail (SYSPROF_IS_MARKS_VIEW (self)); + g_return_if_fail (SYSPROF_IS_MARKS_PAGE (self)); g_return_if_fail (GTK_IS_ADJUSTMENT (hadjustment)); gtk_scrolled_window_set_hadjustment (priv->scroller, hadjustment); diff --git a/src/libsysprof-ui/sysprof-cpu-visualizer-row.h b/src/libsysprof-ui/sysprof-marks-page.h similarity index 55% rename from src/libsysprof-ui/sysprof-cpu-visualizer-row.h rename to src/libsysprof-ui/sysprof-marks-page.h index c8293c45..5070b1ae 100644 --- a/src/libsysprof-ui/sysprof-cpu-visualizer-row.h +++ b/src/libsysprof-ui/sysprof-marks-page.h @@ -1,6 +1,6 @@ -/* sysprof-cpu-visualizer-row.h +/* sysprof-marks-page.h * - * Copyright 2016-2019 Christian Hergert + * Copyright 2019 Christian Hergert * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,22 +20,25 @@ #pragma once -#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION) -# error "Only can be included directly." -#endif - -#include "sysprof-line-visualizer-row.h" - -#include "sysprof-version-macros.h" +#include "sysprof-marks-model.h" +#include "sysprof-page.h" +#include "sysprof-zoom-manager.h" G_BEGIN_DECLS -#define SYSPROF_TYPE_CPU_VISUALIZER_ROW (sysprof_cpu_visualizer_row_get_type()) +#define SYSPROF_TYPE_MARKS_PAGE (sysprof_marks_page_get_type()) -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_FINAL_TYPE (SysprofCpuVisualizerRow, sysprof_cpu_visualizer_row, SYSPROF, CPU_VISUALIZER_ROW, SysprofLineVisualizerRow) +G_DECLARE_DERIVABLE_TYPE (SysprofMarksPage, sysprof_marks_page, SYSPROF, MARKS_PAGE, SysprofPage) -SYSPROF_AVAILABLE_IN_ALL -GtkWidget *sysprof_cpu_visualizer_row_new (void); +struct _SysprofMarksPageClass +{ + SysprofPageClass parent_class; + + /*< private >*/ + gpointer _reserved[16]; +}; + +GtkWidget *sysprof_marks_page_new (SysprofZoomManager *zoom_manager, + SysprofMarksModelKind kind); G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-marks-page.ui b/src/libsysprof-ui/sysprof-marks-page.ui new file mode 100644 index 00000000..a0a84919 --- /dev/null +++ b/src/libsysprof-ui/sysprof-marks-page.ui @@ -0,0 +1,231 @@ + + + + diff --git a/src/libsysprof-ui/sysprof-marks-view.h b/src/libsysprof-ui/sysprof-marks-view.h deleted file mode 100644 index f646c43f..00000000 --- a/src/libsysprof-ui/sysprof-marks-view.h +++ /dev/null @@ -1,55 +0,0 @@ -/* sysprof-marks-view.h - * - * Copyright 2019 Christian Hergert - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -#pragma once - -#include -#include - -G_BEGIN_DECLS - -#define SYSPROF_TYPE_MARKS_VIEW (sysprof_marks_view_get_type()) - -SYSPROF_AVAILABLE_IN_ALL -G_DECLARE_DERIVABLE_TYPE (SysprofMarksView, sysprof_marks_view, SYSPROF, MARKS_VIEW, GtkBin) - -struct _SysprofMarksViewClass -{ - GtkBinClass parent_class; - - /*< private >*/ - gpointer _reserved[16]; -}; - -SYSPROF_AVAILABLE_IN_ALL -GtkWidget *sysprof_marks_view_new (void); -SYSPROF_AVAILABLE_IN_ALL -void sysprof_marks_view_load_async (SysprofMarksView *self, - SysprofCaptureReader *reader, - SysprofSelection *selection, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -SYSPROF_AVAILABLE_IN_ALL -gboolean sysprof_marks_view_load_finish (SysprofMarksView *self, - GAsyncResult *result, - GError **error); - -G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-marks-view.ui b/src/libsysprof-ui/sysprof-marks-view.ui deleted file mode 100644 index 5adb71bd..00000000 --- a/src/libsysprof-ui/sysprof-marks-view.ui +++ /dev/null @@ -1,57 +0,0 @@ - - - - diff --git a/src/libsysprof-ui/sysprof-memory-aid.h b/src/libsysprof-ui/sysprof-memory-aid.h index 424bd65c..d33a1d8c 100644 --- a/src/libsysprof-ui/sysprof-memory-aid.h +++ b/src/libsysprof-ui/sysprof-memory-aid.h @@ -26,10 +26,8 @@ G_BEGIN_DECLS #define SYSPROF_TYPE_MEMORY_AID (sysprof_memory_aid_get_type()) -SYSPROF_AVAILABLE_IN_ALL G_DECLARE_FINAL_TYPE (SysprofMemoryAid, sysprof_memory_aid, SYSPROF, MEMORY_AID, SysprofAid) -SYSPROF_AVAILABLE_IN_ALL SysprofAid *sysprof_memory_aid_new (void); G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-model-filter.h b/src/libsysprof-ui/sysprof-model-filter.h index ddbc2b9a..a1b97bd4 100644 --- a/src/libsysprof-ui/sysprof-model-filter.h +++ b/src/libsysprof-ui/sysprof-model-filter.h @@ -33,7 +33,7 @@ G_BEGIN_DECLS #define SYSPROF_TYPE_MODEL_FILTER (sysprof_model_filter_get_type()) typedef gboolean (*SysprofModelFilterFunc) (GObject *object, - gpointer user_data); + gpointer user_data); SYSPROF_AVAILABLE_IN_ALL G_DECLARE_DERIVABLE_TYPE (SysprofModelFilter, sysprof_model_filter, SYSPROF, MODEL_FILTER, GObject) diff --git a/src/libsysprof-ui/sysprof-notebook.h b/src/libsysprof-ui/sysprof-notebook.h index 2af9d86d..676f4f5e 100644 --- a/src/libsysprof-ui/sysprof-notebook.h +++ b/src/libsysprof-ui/sysprof-notebook.h @@ -20,6 +20,10 @@ #pragma once +#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION) +# error "Only can be included directly." +#endif + #include #include diff --git a/src/libsysprof-ui/sysprof-page.c b/src/libsysprof-ui/sysprof-page.c new file mode 100644 index 00000000..af5f9270 --- /dev/null +++ b/src/libsysprof-ui/sysprof-page.c @@ -0,0 +1,235 @@ +/* sysprof-page.c + * + * Copyright 2019 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#define G_LOG_DOMAIN "sysprof-page" + +#include "config.h" + +#include "sysprof-page.h" + +typedef struct +{ + gchar *title; +} SysprofPagePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (SysprofPage, sysprof_page, GTK_TYPE_BIN) + +enum { + PROP_0, + PROP_TITLE, + N_PROPS +}; + +static GParamSpec *properties [N_PROPS]; + +/** + * sysprof_page_new: + * + * Create a new #SysprofPage. + * + * Returns: (transfer full) (type SysprofPage): a newly created #SysprofPage + */ +GtkWidget * +sysprof_page_new (void) +{ + return g_object_new (SYSPROF_TYPE_PAGE, NULL); +} + +const gchar * +sysprof_page_get_title (SysprofPage *self) +{ + SysprofPagePrivate *priv = sysprof_page_get_instance_private (self); + + g_return_val_if_fail (SYSPROF_IS_PAGE (self), NULL); + + return priv->title; +} + +void +sysprof_page_set_title (SysprofPage *self, + const gchar *title) +{ + SysprofPagePrivate *priv = sysprof_page_get_instance_private (self); + + g_return_if_fail (SYSPROF_IS_PAGE (self)); + + if (g_strcmp0 (priv->title, title) != 0) + { + g_free (priv->title); + priv->title = g_strdup (title); + g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_TITLE]); + } +} + +static void +sysprof_page_real_load_async (SysprofPage *self, + SysprofCaptureReader *reader, + SysprofSelection *selection, + SysprofCaptureCondition *condition, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_task_report_new_error (self, callback, user_data, + sysprof_page_load_async, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "Operation not supported"); +} + +static gboolean +sysprof_page_real_load_finish (SysprofPage *self, + GAsyncResult *result, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (result), error); +} + +static void +sysprof_page_finalize (GObject *object) +{ + SysprofPage *self = (SysprofPage *)object; + SysprofPagePrivate *priv = sysprof_page_get_instance_private (self); + + g_clear_pointer (&priv->title, g_free); + + G_OBJECT_CLASS (sysprof_page_parent_class)->finalize (object); +} + +static void +sysprof_page_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SysprofPage *self = SYSPROF_PAGE (object); + + switch (prop_id) + { + case PROP_TITLE: + g_value_set_string (value, sysprof_page_get_title (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_page_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SysprofPage *self = SYSPROF_PAGE (object); + + switch (prop_id) + { + case PROP_TITLE: + sysprof_page_set_title (self, g_value_get_string (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +sysprof_page_class_init (SysprofPageClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = sysprof_page_finalize; + object_class->get_property = sysprof_page_get_property; + object_class->set_property = sysprof_page_set_property; + + klass->load_async = sysprof_page_real_load_async; + klass->load_finish = sysprof_page_real_load_finish; + + properties [PROP_TITLE] = + g_param_spec_string ("title", + "Title", + "The title for the page", + NULL, + (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +sysprof_page_init (SysprofPage *self) +{ +} + +/** + * sysprof_page_load_async: + * @condition: (nullable): a #sysprofCaptureCondition or %NULL + * @cancellable: (nullable): a #GCancellable or %NULL + * + * Since: 3.34 + */ +void +sysprof_page_load_async (SysprofPage *self, + SysprofCaptureReader *reader, + SysprofSelection *selection, + SysprofCaptureCondition *condition, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail (SYSPROF_IS_PAGE (self)); + g_return_if_fail (reader != NULL); + g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + + SYSPROF_PAGE_GET_CLASS (self)->load_async (self, reader, selection, condition, cancellable, callback, user_data); +} + +gboolean +sysprof_page_load_finish (SysprofPage *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (SYSPROF_IS_PAGE (self), FALSE); + g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); + + return SYSPROF_PAGE_GET_CLASS (self)->load_finish (self, result, error); +} + +void +sysprof_page_set_size_group (SysprofPage *self, + GtkSizeGroup *size_group) +{ + g_return_if_fail (SYSPROF_IS_PAGE (self)); + g_return_if_fail (!size_group || GTK_IS_SIZE_GROUP (size_group)); + + if (SYSPROF_PAGE_GET_CLASS (self)->set_size_group) + SYSPROF_PAGE_GET_CLASS (self)->set_size_group (self, size_group); +} + +void +sysprof_page_set_hadjustment (SysprofPage *self, + GtkAdjustment *hadjustment) +{ + g_return_if_fail (SYSPROF_IS_PAGE (self)); + g_return_if_fail (!hadjustment || GTK_IS_ADJUSTMENT (hadjustment)); + + if (SYSPROF_PAGE_GET_CLASS (self)->set_hadjustment) + SYSPROF_PAGE_GET_CLASS (self)->set_hadjustment (self, hadjustment); +} diff --git a/src/libsysprof-ui/sysprof-page.h b/src/libsysprof-ui/sysprof-page.h new file mode 100644 index 00000000..783d998a --- /dev/null +++ b/src/libsysprof-ui/sysprof-page.h @@ -0,0 +1,86 @@ +/* sysprof-page.h + * + * Copyright 2019 Christian Hergert + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#if !defined (SYSPROF_UI_INSIDE) && !defined (SYSPROF_UI_COMPILATION) +# error "Only can be included directly." +#endif + +#include +#include + +G_BEGIN_DECLS + +#define SYSPROF_TYPE_PAGE (sysprof_page_get_type()) + +SYSPROF_AVAILABLE_IN_ALL +G_DECLARE_DERIVABLE_TYPE (SysprofPage, sysprof_page, SYSPROF, PAGE, GtkBin) + +struct _SysprofPageClass +{ + GtkBinClass parent_class; + + void (*load_async) (SysprofPage *self, + SysprofCaptureReader *reader, + SysprofSelection *selection, + SysprofCaptureCondition *condition, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean (*load_finish) (SysprofPage *self, + GAsyncResult *result, + GError **error); + void (*set_hadjustment) (SysprofPage *self, + GtkAdjustment *hadjustment); + void (*set_size_group) (SysprofPage *self, + GtkSizeGroup *size_group); + + /*< private >*/ + gpointer _reserved[16]; +}; + +SYSPROF_AVAILABLE_IN_ALL +GtkWidget *sysprof_page_new (void); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_page_load_async (SysprofPage *self, + SysprofCaptureReader *reader, + SysprofSelection *selection, + SysprofCaptureCondition *condition, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +SYSPROF_AVAILABLE_IN_ALL +gboolean sysprof_page_load_finish (SysprofPage *self, + GAsyncResult *result, + GError **error); +SYSPROF_AVAILABLE_IN_ALL +const gchar *sysprof_page_get_title (SysprofPage *self); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_page_set_title (SysprofPage *self, + const gchar *title); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_page_set_hadjustment (SysprofPage *self, + GtkAdjustment *hadjustment); +SYSPROF_AVAILABLE_IN_ALL +void sysprof_page_set_size_group (SysprofPage *self, + GtkSizeGroup *size_group); + +G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-profiler-assistant.c b/src/libsysprof-ui/sysprof-profiler-assistant.c index e3db2bf7..a8a66229 100644 --- a/src/libsysprof-ui/sysprof-profiler-assistant.c +++ b/src/libsysprof-ui/sysprof-profiler-assistant.c @@ -27,13 +27,17 @@ #include "sysprof-platform.h" #include "sysprof-aid-icon.h" -#include "sysprof-cpu-aid.h" #include "sysprof-environ-editor.h" #include "sysprof-profiler-assistant.h" -#include "sysprof-proxy-aid.h" #include "sysprof-process-model-row.h" #include "sysprof-ui-private.h" +#include "sysprof-battery-aid.h" +#include "sysprof-callgraph-aid.h" +#include "sysprof-cpu-aid.h" +#include "sysprof-memory-aid.h" +#include "sysprof-proxy-aid.h" + struct _SysprofProfilerAssistant { GtkBin parent_instance; @@ -298,9 +302,12 @@ sysprof_profiler_assistant_class_init (SysprofProfilerAssistantClass *klass) gtk_widget_class_bind_template_child (widget_class, SysprofProfilerAssistant, inherit_switch); g_type_ensure (SYSPROF_TYPE_AID_ICON); + g_type_ensure (SYSPROF_TYPE_BATTERY_AID); g_type_ensure (SYSPROF_TYPE_CPU_AID); + g_type_ensure (SYSPROF_TYPE_MEMORY_AID); g_type_ensure (SYSPROF_TYPE_PROXY_AID); g_type_ensure (SYSPROF_TYPE_ENVIRON_EDITOR); + g_type_ensure (SYSPROF_TYPE_CALLGRAPH_AID); } static void diff --git a/src/libsysprof-ui/sysprof-profiler-assistant.h b/src/libsysprof-ui/sysprof-profiler-assistant.h index 8bc6df57..f57498a2 100644 --- a/src/libsysprof-ui/sysprof-profiler-assistant.h +++ b/src/libsysprof-ui/sysprof-profiler-assistant.h @@ -28,10 +28,8 @@ G_BEGIN_DECLS #define SYSPROF_TYPE_PROFILER_ASSISTANT (sysprof_profiler_assistant_get_type()) -SYSPROF_AVAILABLE_IN_ALL G_DECLARE_FINAL_TYPE (SysprofProfilerAssistant, sysprof_profiler_assistant, SYSPROF, PROFILER_ASSISTANT, GtkBin) -SYSPROF_AVAILABLE_IN_ALL GtkWidget *sysprof_profiler_assistant_new (void); G_END_DECLS diff --git a/src/libsysprof-ui/sysprof-profiler-assistant.ui b/src/libsysprof-ui/sysprof-profiler-assistant.ui index 7a08003c..2a15f308 100644 --- a/src/libsysprof-ui/sysprof-profiler-assistant.ui +++ b/src/libsysprof-ui/sysprof-profiler-assistant.ui @@ -43,13 +43,7 @@ - - Battery - battery-low-charging-symbolic - - - - +