diff options
author | Steven Rostedt <srostedt@redhat.com> | 2010-06-10 17:53:51 -0400 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2010-06-10 17:53:51 -0400 |
commit | 3c95290d3fb593145b8ce1163d795a08f05e112c (patch) | |
tree | e6fc1bea660993e4eaaf5b716bef577d6fbf692e | |
parent | d01b699fffc573e7653e00d608444735c04f9dca (diff) | |
parent | b09e5f4f3fc5c8fc2c51376050af19660c8053f4 (diff) |
Merge branch 'kernelshark-devel' into trace-cmd
Conflicts:
Makefile
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
60 files changed, 3865 insertions, 491 deletions
diff --git a/Documentation/HTML/images/kshark-cursor-1.png b/Documentation/HTML/images/kshark-cursor-1.png new file mode 100644 index 0000000..64d19be --- /dev/null +++ b/Documentation/HTML/images/kshark-cursor-1.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-filter-advance-1.png b/Documentation/HTML/images/kshark-filter-advance-1.png new file mode 100644 index 0000000..23f6584 --- /dev/null +++ b/Documentation/HTML/images/kshark-filter-advance-1.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-filter-del-adv.png b/Documentation/HTML/images/kshark-filter-del-adv.png new file mode 100644 index 0000000..88e2376 --- /dev/null +++ b/Documentation/HTML/images/kshark-filter-del-adv.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-filter-event-adv-list.png b/Documentation/HTML/images/kshark-filter-event-adv-list.png new file mode 100644 index 0000000..c2f2d52 --- /dev/null +++ b/Documentation/HTML/images/kshark-filter-event-adv-list.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-filter-events-sched.png b/Documentation/HTML/images/kshark-filter-events-sched.png new file mode 100644 index 0000000..0941317 --- /dev/null +++ b/Documentation/HTML/images/kshark-filter-events-sched.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-filter-events.png b/Documentation/HTML/images/kshark-filter-events.png new file mode 100644 index 0000000..4732f8a --- /dev/null +++ b/Documentation/HTML/images/kshark-filter-events.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-filter-list-adv-irq.png b/Documentation/HTML/images/kshark-filter-list-adv-irq.png new file mode 100644 index 0000000..0b04031 --- /dev/null +++ b/Documentation/HTML/images/kshark-filter-list-adv-irq.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-filter-sync-graph-1.png b/Documentation/HTML/images/kshark-filter-sync-graph-1.png new file mode 100644 index 0000000..82e5849 --- /dev/null +++ b/Documentation/HTML/images/kshark-filter-sync-graph-1.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-filter-task-menu.png b/Documentation/HTML/images/kshark-filter-task-menu.png new file mode 100644 index 0000000..77fb98a --- /dev/null +++ b/Documentation/HTML/images/kshark-filter-task-menu.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-filter.png b/Documentation/HTML/images/kshark-filter.png new file mode 100644 index 0000000..0e9a380 --- /dev/null +++ b/Documentation/HTML/images/kshark-filter.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-graph-info-line.png b/Documentation/HTML/images/kshark-graph-info-line.png new file mode 100644 index 0000000..4f6be47 --- /dev/null +++ b/Documentation/HTML/images/kshark-graph-info-line.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-graph-plot-area.png b/Documentation/HTML/images/kshark-graph-plot-area.png new file mode 100644 index 0000000..84ed90e --- /dev/null +++ b/Documentation/HTML/images/kshark-graph-plot-area.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-graph-plot-title.png b/Documentation/HTML/images/kshark-graph-plot-title.png new file mode 100644 index 0000000..cbb8d2d --- /dev/null +++ b/Documentation/HTML/images/kshark-graph-plot-title.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-list-adjust.png b/Documentation/HTML/images/kshark-list-adjust.png new file mode 100644 index 0000000..aff15c1 --- /dev/null +++ b/Documentation/HTML/images/kshark-list-adjust.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-list-enable-filter-1.png b/Documentation/HTML/images/kshark-list-enable-filter-1.png new file mode 100644 index 0000000..8321c92 --- /dev/null +++ b/Documentation/HTML/images/kshark-list-enable-filter-1.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-list-graph-follow-1.png b/Documentation/HTML/images/kshark-list-graph-follow-1.png new file mode 100644 index 0000000..c4f8702 --- /dev/null +++ b/Documentation/HTML/images/kshark-list-graph-follow-1.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-list-graph-follow-2.png b/Documentation/HTML/images/kshark-list-graph-follow-2.png new file mode 100644 index 0000000..451de10 --- /dev/null +++ b/Documentation/HTML/images/kshark-list-graph-follow-2.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-list-info-area.png b/Documentation/HTML/images/kshark-list-info-area.png new file mode 100644 index 0000000..394dfdd --- /dev/null +++ b/Documentation/HTML/images/kshark-list-info-area.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-open.png b/Documentation/HTML/images/kshark-open.png new file mode 100644 index 0000000..1b201ce --- /dev/null +++ b/Documentation/HTML/images/kshark-open.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-plot-cpu-1.png b/Documentation/HTML/images/kshark-plot-cpu-1.png new file mode 100644 index 0000000..f49de93 --- /dev/null +++ b/Documentation/HTML/images/kshark-plot-cpu-1.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-plot-cpu-2.png b/Documentation/HTML/images/kshark-plot-cpu-2.png new file mode 100644 index 0000000..c1ea7d1 --- /dev/null +++ b/Documentation/HTML/images/kshark-plot-cpu-2.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-plot-cpu-result.png b/Documentation/HTML/images/kshark-plot-cpu-result.png new file mode 100644 index 0000000..635ddda --- /dev/null +++ b/Documentation/HTML/images/kshark-plot-cpu-result.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-plot-menu.png b/Documentation/HTML/images/kshark-plot-menu.png new file mode 100644 index 0000000..a26f052 --- /dev/null +++ b/Documentation/HTML/images/kshark-plot-menu.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-plot-task-measure-preempt.png b/Documentation/HTML/images/kshark-plot-task-measure-preempt.png new file mode 100644 index 0000000..243aef1 --- /dev/null +++ b/Documentation/HTML/images/kshark-plot-task-measure-preempt.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-plot-task-measure.png b/Documentation/HTML/images/kshark-plot-task-measure.png new file mode 100644 index 0000000..d4b7149 --- /dev/null +++ b/Documentation/HTML/images/kshark-plot-task-measure.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-plot-task-result.png b/Documentation/HTML/images/kshark-plot-task-result.png new file mode 100644 index 0000000..12bbf8b --- /dev/null +++ b/Documentation/HTML/images/kshark-plot-task-result.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-plot-task-select.png b/Documentation/HTML/images/kshark-plot-task-select.png new file mode 100644 index 0000000..be7f365 --- /dev/null +++ b/Documentation/HTML/images/kshark-plot-task-select.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-plot-task-zoom-1.png b/Documentation/HTML/images/kshark-plot-task-zoom-1.png new file mode 100644 index 0000000..716b5c6 --- /dev/null +++ b/Documentation/HTML/images/kshark-plot-task-zoom-1.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-select-a-1.png b/Documentation/HTML/images/kshark-select-a-1.png new file mode 100644 index 0000000..40a6cfa --- /dev/null +++ b/Documentation/HTML/images/kshark-select-a-1.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-select-b-1.png b/Documentation/HTML/images/kshark-select-b-1.png new file mode 100644 index 0000000..df4df41 --- /dev/null +++ b/Documentation/HTML/images/kshark-select-b-1.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-zoom-in-2.png b/Documentation/HTML/images/kshark-zoom-in-2.png new file mode 100644 index 0000000..25235e5 --- /dev/null +++ b/Documentation/HTML/images/kshark-zoom-in-2.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-zoom-in-3.png b/Documentation/HTML/images/kshark-zoom-in-3.png new file mode 100644 index 0000000..2c55319 --- /dev/null +++ b/Documentation/HTML/images/kshark-zoom-in-3.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-zoom-in-select.png b/Documentation/HTML/images/kshark-zoom-in-select.png new file mode 100644 index 0000000..12c2be7 --- /dev/null +++ b/Documentation/HTML/images/kshark-zoom-in-select.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/images/kshark-zoom-out-select.png b/Documentation/HTML/images/kshark-zoom-out-select.png new file mode 100644 index 0000000..25c7cfe --- /dev/null +++ b/Documentation/HTML/images/kshark-zoom-out-select.png | |||
Binary files differ | |||
diff --git a/Documentation/HTML/index.html b/Documentation/HTML/index.html new file mode 100644 index 0000000..12c9957 --- /dev/null +++ b/Documentation/HTML/index.html | |||
@@ -0,0 +1,653 @@ | |||
1 | <html> | ||
2 | |||
3 | <title>KernelShark</title> | ||
4 | |||
5 | <body> | ||
6 | |||
7 | <h1>KernelShark</h1> | ||
8 | |||
9 | <hr><h1><a name="ToC">Table of Contents</a></h1> | ||
10 | <p><b><a name="ToC_1" href="#Introduction">Introduction</a></b></br> | ||
11 | <p><b><a name="ToC_2" href="#graph">The Graph View</a></b></br> | ||
12 | <menu> | ||
13 | <li><a name="ToC_2_1" href="#graph-zoom-in">Zooming In</a> | ||
14 | <li><a name="ToC_2_2" href="#graph-zoom-out">Zooming Out</a> | ||
15 | <li><a name="ToC_2_3" href="#graph-marker">Graph Markers</a> | ||
16 | <li><a name="ToC_2_4" href="#graph-cursor">Graph Cursor</a> | ||
17 | <li><a name="ToC_2_5" href="#graph-plots">Graph Plots</a> | ||
18 | <menu> | ||
19 | <li><a name="ToC_2_5_1" href="#graph-plot-task">Task Plots</a> | ||
20 | <li><a name="ToC_2_5_2" href="#graph-plot-cpu">CPU Plots</a> | ||
21 | </menu> | ||
22 | </menu> | ||
23 | <p><b><a name="ToC_3" href="#list">The List View</a></b></br> | ||
24 | <menu> | ||
25 | <li><a name="ToC_3_1" href="#list-select">Selecting an event</a> | ||
26 | <li><a name="ToC_3_2" href="#list-graph-toggle">Graph follows toggle</a> | ||
27 | </menu> | ||
28 | <p><b><a name="ToC_4" href="#filters">Filters</a></b></br> | ||
29 | <menu> | ||
30 | <li><a name="ToC_4_1" href="#filter-task">Task Filter</a> | ||
31 | <li><a name="ToC_4_2" href="#filter-event">Event Filter</a> | ||
32 | <menu> | ||
33 | <li><a name="ToC_4_2_1" href="#filter-adv-event">Advance Event Filter</a> | ||
34 | </menu> | ||
35 | </menu> | ||
36 | |||
37 | |||
38 | <h1><a name="Introduction">Introduction</a></h1> | ||
39 | |||
40 | <p> | ||
41 | KernelShark is a front end reader of <b>trace-cmd(1)</b> output. "trace-cmd record" | ||
42 | and "trace-cmd extract" create a trace.dat (<b>trace-cmd.dat(5)</b>) file. | ||
43 | <b>kernelshark</b> can read this file and produce a graph and list view of its data. | ||
44 | </p> | ||
45 | |||
46 | <img src="images/kshark-open.png"> | ||
47 | |||
48 | <p> | ||
49 | The application has two main viewing areas split by a paned divider. The top half | ||
50 | is a graphical display of the data and the bottom half is a list view of each | ||
51 | event. Underneath the menu bar is the graph information area: | ||
52 | </p> | ||
53 | |||
54 | <h4><a name="graph-info-line">Graph Info Area</a></h4> | ||
55 | |||
56 | <img src="images/kshark-graph-info-line.png"> | ||
57 | |||
58 | <p> | ||
59 | The graph information line displays the timestamp of various locations. | ||
60 | The Pointer: shows the timestamp of where the mouse pointer is. The Cursor: is | ||
61 | the timestamp of the cursor location. To select a cursor location, double click | ||
62 | on the graph. Marker A is set with a left mouse click and Marker B is set | ||
63 | with a with a left mouse click while holding down the shift key. | ||
64 | </p> | ||
65 | |||
66 | <p> | ||
67 | The graph is broken into two parts, the plot title section: | ||
68 | </p> | ||
69 | |||
70 | <h4><a name="graph-plot-title">Plot Title</a></h4> | ||
71 | |||
72 | <img src="images/kshark-graph-plot-title.png"> | ||
73 | |||
74 | <p> | ||
75 | and the plot area: | ||
76 | </p> | ||
77 | |||
78 | <img src="images/kshark-graph-plot-area.png"> | ||
79 | |||
80 | <p> | ||
81 | The plot area contains the data of the given plot, where plots can be per CPU or | ||
82 | per task. The top of the plot area shows a timeline. The numbers in the timeline | ||
83 | are seconds. The time in the timeline is taken from the timestamps within | ||
84 | the trace.dat file which are architecture dependent. The time usually is the timestamp | ||
85 | from when the system started. | ||
86 | </p> | ||
87 | |||
88 | <p> | ||
89 | Below the graph is the list area. | ||
90 | </p> | ||
91 | |||
92 | <h4><a name="list-area">List Area</a></h4> | ||
93 | |||
94 | <img src="images/kshark-list-info-area.png"> | ||
95 | |||
96 | <p> | ||
97 | The list area contains the Page of the list. The list can hold a maximum of | ||
98 | 1 million entries per page. If the trace data contains more than a million | ||
99 | entries, then the list will have more than one page. | ||
100 | </p> | ||
101 | |||
102 | <p> | ||
103 | The list area also contains a search field to search for elements in the | ||
104 | list. | ||
105 | </p> | ||
106 | |||
107 | <h1><a name="graph">The Graph View</a></h1> | ||
108 | |||
109 | <p> | ||
110 | The graph view of kernelshark shows graphical plots of the data stored in | ||
111 | the trace.dat file. The data plots are per CPU or per task. | ||
112 | When there are too many events within the resolution of the graph, | ||
113 | the plots will appear as a rainbow colored bar. To make more sense out of | ||
114 | the graphs, you need to zoom into a given location to see the details of | ||
115 | that time frame more clearly. | ||
116 | </p> | ||
117 | |||
118 | <h2><a name="graph-zoom-in">Zooming In</a></h2> | ||
119 | |||
120 | <p> | ||
121 | To zoom in, left mouse click and hold and then drag the mouse right, release | ||
122 | to zoom. When you click the left mouse button a line will appear and stay | ||
123 | at that location. When you move the mouse to the right, another line appears | ||
124 | and will follow the mouse. When you release the mouse button, the area | ||
125 | between the two lines become the new width of the screen. That is, the graph | ||
126 | zooms in until the lines match the width of the screen. This allows you to | ||
127 | zoom into a specific location. | ||
128 | </p> | ||
129 | |||
130 | <img src="images/kshark-zoom-in-select.png"> | ||
131 | |||
132 | <p> | ||
133 | The area that you selected will now become the new width of the viewable area. | ||
134 | The smaller the selection, the deeper the zoom. Note, that you must select 10 | ||
135 | pixels to zoom in. Less than 10 pixels will cancel the zoom. You can continue zooming | ||
136 | in until you get more details. | ||
137 | </p> | ||
138 | |||
139 | <img src="images/kshark-zoom-in-3.png"> | ||
140 | |||
141 | <p> | ||
142 | To save on resources, when zooming in, the beginning | ||
143 | and end of the full trace may not be reachable with the horizontal scroll bar. | ||
144 | If a plot contains no events within the reachable area, then the line will be empty, | ||
145 | as CPU 1 is in the above image. | ||
146 | </p> | ||
147 | |||
148 | <p> | ||
149 | CPU 0 shows two tasks that were running. One task | ||
150 | is given a pink/red color and the other a green color. The think colored | ||
151 | horizontal bar represents a task other than idle was running. The small | ||
152 | lines that jet out of the bar are where events occur. | ||
153 | </p> | ||
154 | |||
155 | <p> | ||
156 | If you zoom in enough such that a single event has enough room between itself | ||
157 | and other events, the type of event and the name and PID of the task that was running will appear | ||
158 | over the event. | ||
159 | </p> | ||
160 | |||
161 | <h2><a name="graph-zoom-out">Zooming Out</a></h2> | ||
162 | |||
163 | <p> | ||
164 | To zoom back out, left mouse click and hold and then drag the mouse left. | ||
165 | This time the width between the two lines will become a single pixel. | ||
166 | The farther apart the lines are, the farther the zoom out will be. | ||
167 | Zoom out will stop at the width of the full trace. | ||
168 | </p> | ||
169 | |||
170 | <img src="images/kshark-zoom-out-select.png"> | ||
171 | |||
172 | <p> | ||
173 | When the mouse is over an event, a tool tip will appear showing the event name, | ||
174 | the <a href="#latency">latency data</a>, the event info, the timestamp and the task | ||
175 | name and task process ID. | ||
176 | </p> | ||
177 | |||
178 | <img src="images/kshark-zoom-in-2.png"> | ||
179 | |||
180 | <h2><a name="graph-marker">Graph Markers</a></h2> | ||
181 | |||
182 | <p> | ||
183 | There are two markers that can be placed on the graph as well as a cursor. | ||
184 | Marker A is set by a left mouse click. When a marker is set, the | ||
185 | <a href="#graph-info-line">graph info area</a> will be updated. | ||
186 | Marker A is represented by a green line: | ||
187 | </p> | ||
188 | |||
189 | <img src="images/kshark-select-a-1.png"> | ||
190 | |||
191 | <p> | ||
192 | To set Marker B, press and hold the shift key and click the left mouse button. | ||
193 | Marker B will show up in red. | ||
194 | </p> | ||
195 | |||
196 | <img src="images/kshark-select-b-1.png"> | ||
197 | |||
198 | <p> | ||
199 | When both the A and B markers are set, the <a href="#graph-info-line">graph info area</a> | ||
200 | will show the timestamp of where the A and B markers are, as well as the difference | ||
201 | between the two. All timestamps are in seconds, with a resolution of microseconds. | ||
202 | </p> | ||
203 | |||
204 | <h2><a name="graph-cursor">Graph Cursor</a></h2> | ||
205 | |||
206 | <p> | ||
207 | Double clicking on the graph will set the cursor. The cursor is a blue line, and when | ||
208 | it is set, it will also select the event in the list view that is the closest event at the | ||
209 | timeline of where the cursor was selected. | ||
210 | </p> | ||
211 | |||
212 | <img src="images/kshark-cursor-1.png"> | ||
213 | |||
214 | <p> | ||
215 | The above shows that list item 217448 (sys_exit) was the closest event to where | ||
216 | the cursor was selected. | ||
217 | </p> | ||
218 | |||
219 | <p> | ||
220 | Note that setting the cursor with double click will also set Marker A. | ||
221 | </p> | ||
222 | |||
223 | <h2><a name="graph-plots">Graph Plots</a></h2> | ||
224 | |||
225 | <p> | ||
226 | The graph data is represented by plots. The data on the plots is either CPU specific or | ||
227 | task specific. If it is CPU specific, then the data is the timeline of events that | ||
228 | happened on a given CPU (which CPU is shown in the <a href="#graph-plot-title">plot title area</a>). | ||
229 | If the plot is task specific, then the timeline of events is for the given | ||
230 | task regardless of what CPU it was on at the time. The task name is also shown | ||
231 | in the plot title area. | ||
232 | </p> | ||
233 | |||
234 | <p> | ||
235 | By default, all the CPUs within the loaded trace.dat file are plotted. | ||
236 | There are two ways to plot a task. One way is to right mouse click over a | ||
237 | displayed task in the graph and select the plot option. This will add the | ||
238 | task plot directly underneath the CPU plot that the task was on where | ||
239 | the right mouse click took place. The other way is to use the Plots menu. | ||
240 | </p> | ||
241 | |||
242 | <img src="images/kshark-plot-menu.png"> | ||
243 | |||
244 | <h3><a name="graph-plot-task">Task Plots</a></h3> | ||
245 | |||
246 | <p> | ||
247 | Selecting the "Tasks" menu item will bring up a dialog with all the tasks | ||
248 | that were found in the trace data. | ||
249 | </p> | ||
250 | |||
251 | <img src="images/kshark-plot-task-select.png"> | ||
252 | |||
253 | <p> | ||
254 | Selecting a task in this dialog will add the task plot to the bottom of the graph | ||
255 | area. Unselecting a task in this dialog will remove the plot. | ||
256 | </p> | ||
257 | |||
258 | <img src="images/kshark-plot-task-result.png"> | ||
259 | |||
260 | <p> | ||
261 | The colors in the task plots are different depending on which CPU the task | ||
262 | was on at the time. The CPU plots change colors as different tasks run | ||
263 | on the CPU, and the task plots change color depending on what CPU the task | ||
264 | is running on. This makes it easy to see how much a task | ||
265 | bounces around the CPUs. Zooming in on a task plot also shows some more | ||
266 | characteristics of the task. | ||
267 | </p> | ||
268 | |||
269 | <img src="images/kshark-plot-task-zoom-1.png"> | ||
270 | |||
271 | <p> | ||
272 | The hollow green bar that is shown in front of some events in the task | ||
273 | plot represents when the task was woken up from a sleeping state to | ||
274 | when it actually ran. The hollow red bar between some events shows that | ||
275 | the task was preempted by another task even though that task | ||
276 | was still runnable. | ||
277 | </p> | ||
278 | |||
279 | <p> | ||
280 | Since the hollow green bar shows the wake up latency of the task, the | ||
281 | <a href="#graph-marker">A,B markers</a> can be used to measure that time. | ||
282 | </p> | ||
283 | |||
284 | <img src="images/kshark-plot-task-measure.png"> | ||
285 | |||
286 | <p> | ||
287 | The above shows that the epiphany-browser with PID 28072 had a 479 microsecond wake up | ||
288 | latency. The same can be done with the preemption latency. | ||
289 | </p> | ||
290 | |||
291 | <img src="images/kshark-plot-task-measure-preempt.png"> | ||
292 | |||
293 | <h3><a name="graph-plot-cpu">CPU Plots</a></h3> | ||
294 | |||
295 | <p> | ||
296 | Selecting the "CPUs" plot menu item pops up a dialog that shows the available CPUs that | ||
297 | can be plotted. | ||
298 | <p> | ||
299 | |||
300 | <img src="images/kshark-plot-cpu-1.png"> | ||
301 | |||
302 | <p> | ||
303 | Removing a selected CPU and hitting "Apply" will remove that CPU plot. | ||
304 | </p> | ||
305 | |||
306 | <img src="images/kshark-plot-cpu-2.png"> | ||
307 | |||
308 | <p> | ||
309 | |||
310 | <img src="images/kshark-plot-cpu-result.png"> | ||
311 | |||
312 | <h1><a name="list">The List View</a></h1> | ||
313 | |||
314 | <p> | ||
315 | The list view is in the bottom half paned window and can be expanded or shortened | ||
316 | with the paned handle. | ||
317 | </p> | ||
318 | |||
319 | <img src="images/kshark-list-adjust.png"> | ||
320 | |||
321 | <p> | ||
322 | The top of the list view contains the <a href="#list-area">list area</a> which has the list page, list | ||
323 | search, and "graph follows" toggle button. If more than a million events are stored in the | ||
324 | list, then each set of million will be on a different page. | ||
325 | </p> | ||
326 | |||
327 | <p> | ||
328 | The columns of the list are: | ||
329 | </p> | ||
330 | |||
331 | <ul> | ||
332 | <li><b>#</b> - the index into the list. | ||
333 | <li><b>CPU</b> - the CPU that the event occurred on. | ||
334 | <li><b>Time Stamp</b> - The timestamp of the event. This is in seconds with microsecond | ||
335 | resolution. | ||
336 | <li><b>PID</b> - The process ID of the task that was running when the event occurred. | ||
337 | <li><b><a name="latency">Latency</a></b> - The latency is broken into 5 fields: | ||
338 | <ol> | ||
339 | <li>Interrupts disabled - 'd' if interrupts are disabled, otherwise '.' | ||
340 | <li>Need reschedule - 'N' if the kernel was notified that a schedule is needed, otherwise '.' | ||
341 | <li>In IRQ - 'h' if in a hard IRQ (hardware triggered), 's' if in a soft IRQ | ||
342 | (context where the kernel initiated a the IRQ handler) or if soft IRQs | ||
343 | are disabled, 'H' if in a hard IRQ and soft IRQs are disabled or the hard IRQ | ||
344 | triggered while processing a soft IRQ, otherwise '.' | ||
345 | <li>Preemption counter - The index of the preemption counter. If it is other | ||
346 | than zero, then the kernel will not preempt the running tasks, even | ||
347 | if a schedule has been requested by some event. If the counter is zero, | ||
348 | then '.' is shown. | ||
349 | <li>Lock depth - The depth of the big kernel lock being held. The big kernel | ||
350 | lock is recursive (same task may acquire it multiple times). On the first | ||
351 | acquisition, the depth is zero. This field will be zero or greater, or | ||
352 | '.' if the big kernel lock is not held. When the big kernel lock is | ||
353 | finally removed from the kernel, this field will go away as well. | ||
354 | </ol> | ||
355 | <li><b>Event</b> - The name of the event. | ||
356 | <li><b>Info</b> - The data output of a particular event. | ||
357 | </ul> | ||
358 | |||
359 | <p> | ||
360 | The list search can find an event based on the contents in a row. Select a column, a | ||
361 | match criteria and the content to match to find the next row that matches the | ||
362 | search. The match criterion is one of the following: | ||
363 | </p> | ||
364 | |||
365 | <ul> | ||
366 | <li><b>contains</b> - the row cell of the selected column contains the match data. This works with numbers | ||
367 | as well. | ||
368 | <li><b>full match</b> - the row cell of the selected column matches exactly the match data. | ||
369 | <li><b>does not have</b> - the row cell of the selected column does not contain the match data. | ||
370 | </ul> | ||
371 | |||
372 | <p> | ||
373 | The search will find the next event that has the matching data within the column. | ||
374 | </p> | ||
375 | |||
376 | <h2><a name="list-select">Selecting an event</a></h2> | ||
377 | <p> | ||
378 | A single click on a row will select the row, but a double click on a row will select | ||
379 | that row as well as set the graph cursor to the location of that event. If the plot | ||
380 | that the event is on is not visible then the graph will adjust its vertical view area | ||
381 | to show the plot with the selected event. This has no effect on | ||
382 | <a href="#graph-marker">graph markers</a>. | ||
383 | <p> | ||
384 | |||
385 | <h2><a name="list-graph-toggle">Graph follows toggle</a></h2> | ||
386 | |||
387 | <p> | ||
388 | When the "graph follows" toggle is set, then even a single click on a row | ||
389 | will move the graph cursor. With the mouse focus on the list, using the keyboard | ||
390 | up and down arrow keys will move the selection of the list as well as the graph | ||
391 | cursor. | ||
392 | </p> | ||
393 | |||
394 | <img src="images/kshark-list-graph-follow-1.png"> | ||
395 | <p> | ||
396 | <img src="images/kshark-list-graph-follow-2.png"> | ||
397 | |||
398 | <h1><a name="filters">Filters</a></h1> | ||
399 | |||
400 | <p> | ||
401 | The amount of data that can be stored in a trace.dat file can be enormous. | ||
402 | To make any sense out of some traces, it may be required to only display various | ||
403 | events. The same can be true about tasks. | ||
404 | Kernelshark has filters for tasks as well as for events. The task filter | ||
405 | affects both the graph and the list, but the graph and list each have a separate | ||
406 | event filter. | ||
407 | <p> | ||
408 | |||
409 | <h2><a name="filter-task">Task Filter</a></h2> | ||
410 | |||
411 | <p> | ||
412 | The task filter is currently set by a right mouse click over | ||
413 | an event on either the graph or the list view, and by selecting the option to add or remove the | ||
414 | task to/from the task filter. The tasks within the task filter are the same for | ||
415 | both the graph and list, but each can be enabled separately. | ||
416 | </p> | ||
417 | |||
418 | <p> | ||
419 | There are two types of task filters: | ||
420 | </p> | ||
421 | |||
422 | <ul> | ||
423 | <li>Task Filter - only show tasks that are in this filter | ||
424 | <li>Hide Tasks - do not display the tasks within this filter | ||
425 | </ul> | ||
426 | |||
427 | <p> | ||
428 | If there are any tasks within the Task Filter then only those tasks will be displayed | ||
429 | when the filter is enabled. Any task within the Hide Tasks filter will not be | ||
430 | displayed, even if that same task is in the Task Filter. | ||
431 | </p> | ||
432 | |||
433 | <img src="images/kshark-filter-task-menu.png"> | ||
434 | |||
435 | <p> | ||
436 | When either filter contains a task, the filter can be enabled. | ||
437 | </p> | ||
438 | |||
439 | <img src="images/kshark-list-enable-filter-1.png"> | ||
440 | |||
441 | <p> | ||
442 | When a task is not in the "Task Filter", the pop up will show the | ||
443 | menu item "Add task". When a task is in the "Task Filter" the | ||
444 | pop up will show "Remove task". | ||
445 | </p> | ||
446 | |||
447 | <p> | ||
448 | When a task is not in the "Hide Tasks", the pop up will show the | ||
449 | menu item "Hide task". When a task is in the "Hide Tasks", the | ||
450 | pop up will show "Show task". | ||
451 | </p> | ||
452 | |||
453 | <h4>The scheduling events</h4> | ||
454 | |||
455 | <p> | ||
456 | The events "sched_switch", "sched_wakeup", and "sched_wakeup_new" are treated | ||
457 | differently by the task filter. Because these events deal with two tasks | ||
458 | (previous and next, waker and wakee), if either of the tasks should be visible | ||
459 | then that event is visible regardless if the other task should be hidden. | ||
460 | This may seem confusing when an event that is hidden shows up in the Task column. | ||
461 | </p> | ||
462 | |||
463 | <h2><a name="filter-event">Event Filter</a></h2> | ||
464 | |||
465 | <p> | ||
466 | The graph and list view each have their own event filter. The event filters | ||
467 | are enabled through the Filter menu on the top menu-bar. | ||
468 | </p> | ||
469 | |||
470 | <img src="images/kshark-filter.png"> | ||
471 | |||
472 | <p> | ||
473 | Selecting either the "list events" or "graph events" will bring up the event | ||
474 | dialog with the events that are visible selected. | ||
475 | </p> | ||
476 | |||
477 | <p> | ||
478 | Note: these do not mean that the events exist in the trace.dat file. They are | ||
479 | selected if the events are not to be hidden. Events that do not exist in the trace | ||
480 | will not be displayed regardless of whether or not they are filtered. | ||
481 | </p> | ||
482 | |||
483 | <img src="images/kshark-filter-events.png"> | ||
484 | |||
485 | <p> | ||
486 | Clicking on "All" or any of the systems will either deselect all events underneath | ||
487 | or select all events underneath depending on the previous state of the box being | ||
488 | selected. By deselecting all events, it makes it easier to enable just a few individual events. | ||
489 | <p> | ||
490 | |||
491 | <img src="images/kshark-filter-events-sched.png"> | ||
492 | |||
493 | <p> | ||
494 | If it is desired that the graph and list view have the same events filtered, then just | ||
495 | set up the desired filtering in one and then synchronize the other through | ||
496 | the filter menu. | ||
497 | </p> | ||
498 | |||
499 | <img src="images/kshark-filter-sync-graph-1.png"> | ||
500 | |||
501 | <ul> | ||
502 | <li><b>sync graph events with list</b> - will set the graph event filter to be the same | ||
503 | as what is in the list filter. | ||
504 | <li><b>sync list events with graph</b> - will set the list event filter to be the same | ||
505 | as what is in the graph filter. | ||
506 | </ul> | ||
507 | |||
508 | <h3><a name="filter-adv-event">Advanced Event Filter</a></h3> | ||
509 | |||
510 | <p> | ||
511 | Filtering on events may not be enough. The ability to filter on the content | ||
512 | of individual events may be needed. In order to accomplish this, the advanced event | ||
513 | filtering is used. Selecting the "list advanced event" or "graph advanced event" | ||
514 | from the Filter menu will pop up the advanced event filtering dialog. | ||
515 | The graph and list view each have their own advanced event filter. | ||
516 | </p> | ||
517 | |||
518 | <img src="images/kshark-filter-advance-1.png"> | ||
519 | |||
520 | <p> | ||
521 | The "Filter:" entry at the bottom of the dialog is where the advanced filter is | ||
522 | written. Above that is helper buttons to pick events, operations and event fields. | ||
523 | The syntax of the filter is: | ||
524 | <p> | ||
525 | |||
526 | <pre> | ||
527 | FILTER := EVENTS | EVENTS ':' EXPRESSION | ||
528 | EVENTS := EVENTS ',' EVENTS | SYSTEM '/' EVENT | SYSTEM | EVENT | ||
529 | SYSTEM := any system name | ||
530 | EVENT := any event name | ||
531 | EXPRESSION := EXPRESSION BOOL EXPRESSION | '(' EXPRESSION ')' | OPERATION | ||
532 | BOOL := '&&' | '||' | ||
533 | OPERATION := '!' EXPRESSION | LVALUE CMP RVALUE | LVALUE STRCMP STRVALUE | ||
534 | CMP := '>' | '<' | '==' | '>=' | '<=' | '!=' | ||
535 | STRCMP := '==' | '!=' | '=~' | '!~' | ||
536 | RVALUE := integer | FIELD | ||
537 | STRVALUE := string (double quoted value) | FIELD | ||
538 | LVALUE := FIELD | EXPR | ||
539 | EXPR := FIELD OP RVALUE | '(' EXPR ')' | EXPR OP EXPR | ||
540 | FIELD := a field name of an event | ||
541 | OP := '+' | '-' | '*' | '/' | '<<' | '>>' | '&' | '!' | ||
542 | </pre> | ||
543 | |||
544 | <p> | ||
545 | Spaces are ignored. The example used in the dialog figure: | ||
546 | </p> | ||
547 | |||
548 | <pre> | ||
549 | sched/sched_switch : next_prio < 100 && (prev_prio > 100 && prev_pid != 0) | ||
550 | </pre> | ||
551 | |||
552 | <p> | ||
553 | The <tt>sched/</tt> is not necessary because without it, the filter will process all events | ||
554 | named <tt>sched_switch</tt>, and since there is only one event | ||
555 | that has that name, including the <tt>sched/</tt> is redundant. | ||
556 | <p> | ||
557 | |||
558 | <p> | ||
559 | The <tt>next_prio</tt>, <tt>prev_prio</tt> and <tt>prev_pid</tt> are all | ||
560 | event fields of the <tt>sched_swich</tt> event. | ||
561 | </p> | ||
562 | |||
563 | <p> | ||
564 | If just <tt>sched</tt> was used and the <tt>/sched_switch</tt> was omitted, it would | ||
565 | still be a valid filter, but it would behave differently. By just specifying | ||
566 | a system, the filter will run on all events within that system. When a field | ||
567 | is encountered that does not belong to an event, then that compare will be set to false. | ||
568 | </p> | ||
569 | |||
570 | <pre> | ||
571 | sched : prev_pid != 0 | ||
572 | sched : !(prev_pid == 0) | ||
573 | </pre> | ||
574 | |||
575 | <p> | ||
576 | The above two filters are not equivalent. They are for the <tt>sched_switch</tt> event, | ||
577 | but not for the other events. The first filter will return false for all events | ||
578 | that do not contain the <tt>prev_pid</tt> field, but the second filter would return | ||
579 | true for all events that do not contain that field. Again, if the event does | ||
580 | not contain a field, just that compare will be evaluated to false, not the entire | ||
581 | expression. This means for events that do not have the <tt>prev_pid</tt> field, | ||
582 | the above filters would be equivalent to: | ||
583 | </p> | ||
584 | |||
585 | <pre> | ||
586 | sched : FALSE | ||
587 | sched : !(FALSE) | ||
588 | </pre> | ||
589 | |||
590 | <p> | ||
591 | Letting filters contain fields that do not belong to an event be valid | ||
592 | allows for various tricks where two events can share the same | ||
593 | filter. | ||
594 | </p> | ||
595 | |||
596 | <pre> | ||
597 | sched_switch, sched_wake.* : next_pid == 1 || pid == 1 | ||
598 | </pre> | ||
599 | |||
600 | <p> | ||
601 | The schedule events that have <tt>next_pid</tt> and not <tt>pid</tt> as a field | ||
602 | will just compare the first part of the <tt>||</tt> and those events with | ||
603 | <tt>pid</tt> but without <tt>next_pid</tt> will be compared against the second | ||
604 | part of the <tt>||</tt> | ||
605 | </p> | ||
606 | |||
607 | <p> | ||
608 | Notice that event names in the filter can be regular expressions. | ||
609 | </p> | ||
610 | |||
611 | <p> | ||
612 | String fields can have regular expressions used in their comparing if | ||
613 | <tt>=~</tt> or <tt>!~</tt> are used. | ||
614 | </p> | ||
615 | |||
616 | <pre> | ||
617 | sched_switch : next_comm =~ "^events/[23]$" | ||
618 | </pre> | ||
619 | |||
620 | <p> | ||
621 | The available regular expressions are described in <b>regex(7)</b>. | ||
622 | </p> | ||
623 | |||
624 | <p> | ||
625 | Note: When adding an advanced filter, all non-advanced filters | ||
626 | (added by the event filter dialog box) will be removed, and only the advanced | ||
627 | filters will stay. But non-advanced filters may be added after advanced | ||
628 | filters have been. The events that have advanced filters will be shaded | ||
629 | in the event filter dialog: | ||
630 | <p> | ||
631 | |||
632 | <img src="images/kshark-filter-event-adv-list.png"> | ||
633 | |||
634 | <p> | ||
635 | Just do not click on the advanced filter box and hit "Apply" unless you want to remove | ||
636 | the advanced filter. Non-advanced filters can now be selected without affecting | ||
637 | the advanced filters. | ||
638 | </p> | ||
639 | |||
640 | <img src="images/kshark-filter-list-adv-irq.png"> | ||
641 | |||
642 | <p> | ||
643 | When advanced filters already exist when the advanced filter dialog box pops up, | ||
644 | they will be listed in the area at the top of the dialog. Although | ||
645 | one filter may have been written, the list will be per event. A check box | ||
646 | is to the left of the filter. When checked, the filter will be deleted if | ||
647 | the "Apply" button is selected. | ||
648 | </p> | ||
649 | |||
650 | <img src="images/kshark-filter-del-adv.png"> | ||
651 | |||
652 | </body> | ||
653 | </html> | ||
diff --git a/Documentation/Makefile b/Documentation/Makefile index b7c17e7..be3711f 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile | |||
@@ -87,7 +87,25 @@ $(MAN1_INSTALL): %.1.install : %.1 force | |||
87 | $(MAN5_INSTALL): %.5.install : %.5 force | 87 | $(MAN5_INSTALL): %.5.install : %.5 force |
88 | $(Q)$(call do_install,$<,$(man_dir_SQ)/man5) | 88 | $(Q)$(call do_install,$<,$(man_dir_SQ)/man5) |
89 | 89 | ||
90 | install: $(MAN1_INSTALL) $(MAN5_INSTALL) | 90 | html_dir = $(src)/HTML |
91 | image_dir = $(html_dir)/images | ||
92 | |||
93 | HTML = $(wildcard $(html_dir)/*.html) | ||
94 | IMGS = $(wildcard $(image_dir)/*.png) | ||
95 | |||
96 | HTML_INSTALL = $(subst .html,.html.install,$(HTML)) | ||
97 | IMGS_INSTALL = $(subst .png,.png.install,$(IMGS)) | ||
98 | |||
99 | $(HTML_INSTALL): %.html.install : %.html force | ||
100 | $(Q)$(call do_install, $<, '$(html_install_SQ)') | ||
101 | |||
102 | $(IMGS_INSTALL): %.png.install : %.png force | ||
103 | $(Q)$(call do_install, $<, '$(img_install_SQ)') | ||
104 | |||
105 | |||
106 | GUI_INSTALL = $(HTML_INSTALL) $(IMGS_INSTALL) | ||
107 | |||
108 | install: $(MAN1_INSTALL) $(MAN5_INSTALL) $(GUI_INSTALL) | ||
91 | 109 | ||
92 | clean: | 110 | clean: |
93 | (cd $(obj); \ | 111 | (cd $(obj); \ |
@@ -30,8 +30,13 @@ bindir_relative = bin | |||
30 | bindir = $(prefix)/$(bindir_relative) | 30 | bindir = $(prefix)/$(bindir_relative) |
31 | man_dir = $(prefix)/share/man | 31 | man_dir = $(prefix)/share/man |
32 | man_dir_SQ = '$(subst ','\'',$(man_dir))' | 32 | man_dir_SQ = '$(subst ','\'',$(man_dir))' |
33 | html_install = $(prefix)/share/kernelshark/html | ||
34 | html_install_SQ = '$(subst ','\'',$(html_install))' | ||
35 | img_install = $(prefix)/share/kernelshark/html/images | ||
36 | img_install_SQ = '$(subst ','\'',$(img_install))' | ||
33 | 37 | ||
34 | export man_dir man_dir_SQ INSTALL | 38 | export man_dir man_dir_SQ html_install html_install_SQ INSTALL |
39 | export img_install img_install_SQ | ||
35 | export DESTDIR DESTDIR_SQ | 40 | export DESTDIR DESTDIR_SQ |
36 | 41 | ||
37 | ifeq ($(prefix),$(HOME)) | 42 | ifeq ($(prefix),$(HOME)) |
@@ -46,6 +51,9 @@ PLUGIN_DIR_SQ = '$(subst ','\'',$(PLUGIN_DIR))' | |||
46 | PYTHON_DIR_SQ = '$(subst ','\'',$(PYTHON_DIR))' | 51 | PYTHON_DIR_SQ = '$(subst ','\'',$(PYTHON_DIR))' |
47 | endif | 52 | endif |
48 | 53 | ||
54 | HELP_DIR = -DHELP_DIR=$(html_install) | ||
55 | HELP_DIR_SQ = '$(subst ','\'',$(HELP_DIR))' | ||
56 | |||
49 | # copy a bit from Linux kbuild | 57 | # copy a bit from Linux kbuild |
50 | 58 | ||
51 | ifeq ("$(origin V)", "command line") | 59 | ifeq ("$(origin V)", "command line") |
@@ -120,7 +128,7 @@ python_dir_SQ = $(subst ','\'',$(python_dir)) | |||
120 | LIBS = -L. -ltracecmd -ldl | 128 | LIBS = -L. -ltracecmd -ldl |
121 | LIB_FILE = libtracecmd.a | 129 | LIB_FILE = libtracecmd.a |
122 | 130 | ||
123 | PACKAGES= gtk+-2.0 | 131 | PACKAGES= gtk+-2.0 libxml-2.0 |
124 | 132 | ||
125 | ifndef BUILDGUI | 133 | ifndef BUILDGUI |
126 | BUILDGUI = 0 | 134 | BUILDGUI = 0 |
@@ -148,6 +156,7 @@ REBUILD_GUI = /bin/true | |||
148 | G = | 156 | G = |
149 | N = @/bin/true || | 157 | N = @/bin/true || |
150 | 158 | ||
159 | CONFIG_FLAGS += $(HELP_DIR_SQ) | ||
151 | else | 160 | else |
152 | 161 | ||
153 | CONFIG_INCLUDES = | 162 | CONFIG_INCLUDES = |
@@ -237,16 +246,17 @@ $(obj)/%.o: $(src)/%.c | |||
237 | %.o: $(src)/%.c | 246 | %.o: $(src)/%.c |
238 | $(Q)$(call check_gui) | 247 | $(Q)$(call check_gui) |
239 | 248 | ||
249 | TRACE_GUI_OBJS = trace-filter.o trace-compat.o trace-hash.o trace-dialog.o \ | ||
250 | trace-xml.o | ||
240 | TRACE_CMD_OBJS = trace-cmd.o trace-usage.o trace-read.o trace-split.o trace-listen.o | 251 | TRACE_CMD_OBJS = trace-cmd.o trace-usage.o trace-read.o trace-split.o trace-listen.o |
241 | TRACE_VIEW_OBJS = trace-view.o trace-view-store.o trace-filter.o trace-compat.o \ | 252 | TRACE_VIEW_OBJS = trace-view.o trace-view-store.o |
242 | trace-hash.o | 253 | TRACE_GRAPH_OBJS = trace-graph.o trace-plot.o trace-plot-cpu.o trace-plot-task.o |
243 | TRACE_GRAPH_OBJS = trace-graph.o trace-compat.o trace-hash.o trace-filter.o \ | 254 | TRACE_VIEW_MAIN_OBJS = trace-view-main.o $(TRACE_VIEW_OBJS) $(TRACE_GUI_OBJS) |
244 | trace-plot.o trace-plot-cpu.o trace-plot-task.o | 255 | TRACE_GRAPH_MAIN_OBJS = trace-graph-main.o $(TRACE_GRAPH_OBJS) $(TRACE_GUI_OBJS) |
245 | TRACE_VIEW_MAIN_OBJS = trace-view-main.o $(TRACE_VIEW_OBJS) | 256 | KERNEL_SHARK_OBJS = $(TRACE_VIEW_OBJS) $(TRACE_GRAPH_OBJS) $(TRACE_GUI_OBJS) \ |
246 | TRACE_GRAPH_MAIN_OBJS = trace-graph-main.o $(TRACE_GRAPH_OBJS) | 257 | kernel-shark.o |
247 | KERNEL_SHARK_OBJS = $(TRACE_VIEW_OBJS) $(TRACE_GRAPH_OBJS) kernel-shark.o | 258 | |
248 | 259 | PEVENT_LIB_OBJS = parse-events.o trace-seq.o parse-filter.o parse-utils.o | |
249 | PEVENT_LIB_OBJS = parse-events.o trace-seq.o parse-filter.o | ||
250 | TCMD_LIB_OBJS = $(PEVENT_LIB_OBJS) trace-util.o trace-input.o trace-ftrace.o \ | 260 | TCMD_LIB_OBJS = $(PEVENT_LIB_OBJS) trace-util.o trace-input.o trace-ftrace.o \ |
251 | trace-output.o trace-record.o | 261 | trace-output.o trace-record.o |
252 | 262 | ||
diff --git a/kernel-shark.c b/kernel-shark.c index fcb0f54..9ca3942 100644 --- a/kernel-shark.c +++ b/kernel-shark.c | |||
@@ -27,13 +27,18 @@ | |||
27 | #include <sys/stat.h> | 27 | #include <sys/stat.h> |
28 | #include <unistd.h> | 28 | #include <unistd.h> |
29 | #include <gtk/gtk.h> | 29 | #include <gtk/gtk.h> |
30 | #include <errno.h> | ||
30 | #include <getopt.h> | 31 | #include <getopt.h> |
31 | 32 | ||
32 | #include "trace-compat.h" | 33 | #include "trace-compat.h" |
33 | #include "trace-cmd.h" | 34 | #include "trace-cmd.h" |
35 | #include "trace-gui.h" | ||
34 | #include "kernel-shark.h" | 36 | #include "kernel-shark.h" |
35 | #include "version.h" | 37 | #include "version.h" |
36 | 38 | ||
39 | #define ___stringify(X) #X | ||
40 | #define __stringify(X) ___stringify(X) | ||
41 | |||
37 | #define DEBUG_LEVEL 0 | 42 | #define DEBUG_LEVEL 0 |
38 | #if DEBUG_LEVEL > 0 | 43 | #if DEBUG_LEVEL > 0 |
39 | # define dprintf(l, x...) \ | 44 | # define dprintf(l, x...) \ |
@@ -59,6 +64,85 @@ void usage(char *prog) | |||
59 | printf(" -i input_file, default is %s\n", default_input_file); | 64 | printf(" -i input_file, default is %s\n", default_input_file); |
60 | } | 65 | } |
61 | 66 | ||
67 | /* | ||
68 | * trace_sync_select_menu - helper function to the syncing of list and graph filters | ||
69 | * | ||
70 | * Creates a pop up dialog with the selections given. The selections will be | ||
71 | * radio buttons to the user. The keep is a value that will be set to the check | ||
72 | * box (default on) if the user wants to keep the selection persistant. | ||
73 | */ | ||
74 | static int trace_sync_select_menu(const gchar *title, | ||
75 | gchar **selections, gboolean *keep) | ||
76 | { | ||
77 | GtkWidget *dialog; | ||
78 | GtkWidget *radio; | ||
79 | GtkWidget *check; | ||
80 | GSList *group; | ||
81 | int result; | ||
82 | int i; | ||
83 | |||
84 | dialog = gtk_dialog_new_with_buttons(title, | ||
85 | NULL, | ||
86 | GTK_DIALOG_MODAL, | ||
87 | "OK", GTK_RESPONSE_ACCEPT, | ||
88 | GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | ||
89 | NULL); | ||
90 | |||
91 | radio = gtk_radio_button_new_with_label(NULL, selections[0]); | ||
92 | gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), radio, TRUE, TRUE, 0); | ||
93 | gtk_widget_show(radio); | ||
94 | |||
95 | group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio)); | ||
96 | |||
97 | for (i = 1; selections[i]; i++) { | ||
98 | radio = gtk_radio_button_new_with_label(group, selections[i]); | ||
99 | gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), radio, TRUE, TRUE, 0); | ||
100 | gtk_widget_show(radio); | ||
101 | } | ||
102 | |||
103 | check = gtk_check_button_new_with_label("Keep the filters in sync?"); | ||
104 | gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), check, TRUE, TRUE, 0); | ||
105 | gtk_widget_show(check); | ||
106 | |||
107 | gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), TRUE); | ||
108 | |||
109 | result = gtk_dialog_run(GTK_DIALOG(dialog)); | ||
110 | switch (result) { | ||
111 | case GTK_RESPONSE_ACCEPT: | ||
112 | i = 0; | ||
113 | for (i = 0; group; i++, group = g_slist_next(group)) { | ||
114 | radio = group->data; | ||
115 | if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radio))) | ||
116 | break; | ||
117 | } | ||
118 | result = i; | ||
119 | *keep = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check)); | ||
120 | break; | ||
121 | default: | ||
122 | result = -1; | ||
123 | } | ||
124 | |||
125 | gtk_widget_destroy(dialog); | ||
126 | return result; | ||
127 | } | ||
128 | |||
129 | static void update_tree_view_filters(struct shark_info *info, | ||
130 | struct filter_task *task_filter, | ||
131 | struct filter_task *hide_tasks) | ||
132 | { | ||
133 | if (info->list_filter_enabled) | ||
134 | trace_view_update_filters(info->treeview, | ||
135 | task_filter, hide_tasks); | ||
136 | |||
137 | if (filter_task_count(task_filter) || | ||
138 | filter_task_count(hide_tasks)) | ||
139 | info->list_filter_available = 1; | ||
140 | else { | ||
141 | info->list_filter_enabled = 0; | ||
142 | info->list_filter_available = 0; | ||
143 | } | ||
144 | } | ||
145 | |||
62 | /* graph callbacks */ | 146 | /* graph callbacks */ |
63 | 147 | ||
64 | /* convert_nano() and print_time() are copied from trace-graph.c for debugging | 148 | /* convert_nano() and print_time() are copied from trace-graph.c for debugging |
@@ -108,15 +192,20 @@ static void ks_graph_filter(struct graph_info *ginfo, | |||
108 | cbs = trace_graph_get_callbacks(ginfo); | 192 | cbs = trace_graph_get_callbacks(ginfo); |
109 | info = container_of(cbs, struct shark_info, graph_cbs); | 193 | info = container_of(cbs, struct shark_info, graph_cbs); |
110 | 194 | ||
111 | if (info->list_filter_enabled) | 195 | if (!info->sync_task_filters) |
112 | trace_view_update_filters(info->treeview, | 196 | return; |
113 | task_filter, hide_tasks); | 197 | |
198 | update_tree_view_filters(info, task_filter, hide_tasks); | ||
114 | } | 199 | } |
115 | 200 | ||
116 | static void free_info(struct shark_info *info) | 201 | static void free_info(struct shark_info *info) |
117 | { | 202 | { |
118 | tracecmd_close(info->handle); | 203 | tracecmd_close(info->handle); |
119 | trace_graph_free_info(info->ginfo); | 204 | trace_graph_free_info(info->ginfo); |
205 | |||
206 | filter_task_hash_free(info->list_task_filter); | ||
207 | filter_task_hash_free(info->list_hide_tasks); | ||
208 | |||
120 | free(info->ginfo); | 209 | free(info->ginfo); |
121 | free(info); | 210 | free(info); |
122 | } | 211 | } |
@@ -134,34 +223,219 @@ static void update_title(GtkWidget *window, const gchar *file) | |||
134 | g_free(str); | 223 | g_free(str); |
135 | } | 224 | } |
136 | 225 | ||
226 | static void unsync_task_filters(struct shark_info *info) | ||
227 | { | ||
228 | info->sync_task_filters = 0; | ||
229 | gtk_menu_item_set_label(GTK_MENU_ITEM(info->task_sync_menu), | ||
230 | "Sync Graph and List Task Filters"); | ||
231 | |||
232 | gtk_menu_item_set_label(GTK_MENU_ITEM(info->graph_task_menu), | ||
233 | "graph tasks"); | ||
234 | gtk_menu_item_set_label(GTK_MENU_ITEM(info->graph_hide_task_menu), | ||
235 | "graph hide tasks"); | ||
236 | gtk_widget_show(info->list_task_menu); | ||
237 | gtk_widget_show(info->list_hide_task_menu); | ||
238 | |||
239 | /* The list now uses its own hash */ | ||
240 | info->list_task_filter = filter_task_hash_copy(info->ginfo->task_filter); | ||
241 | info->list_hide_tasks = filter_task_hash_copy(info->ginfo->hide_tasks); | ||
242 | } | ||
243 | |||
244 | static void sync_task_filters(struct shark_info *info) | ||
245 | { | ||
246 | info->sync_task_filters = 1; | ||
247 | gtk_menu_item_set_label(GTK_MENU_ITEM(info->task_sync_menu), | ||
248 | "Unsync Graph and List Task Filters"); | ||
249 | gtk_menu_item_set_label(GTK_MENU_ITEM(info->graph_task_menu), | ||
250 | "tasks"); | ||
251 | gtk_menu_item_set_label(GTK_MENU_ITEM(info->graph_hide_task_menu), | ||
252 | "hide tasks"); | ||
253 | gtk_widget_hide(info->list_task_menu); | ||
254 | gtk_widget_hide(info->list_hide_task_menu); | ||
255 | } | ||
256 | |||
257 | static void unsync_event_filters(struct shark_info *info) | ||
258 | { | ||
259 | info->sync_event_filters = 0; | ||
260 | gtk_menu_item_set_label(GTK_MENU_ITEM(info->events_sync_menu), | ||
261 | "Sync Graph and List Event Filters"); | ||
262 | |||
263 | gtk_menu_item_set_label(GTK_MENU_ITEM(info->graph_events_menu), | ||
264 | "graph events"); | ||
265 | gtk_menu_item_set_label(GTK_MENU_ITEM(info->graph_adv_events_menu), | ||
266 | "graph advanced events"); | ||
267 | gtk_widget_show(info->list_events_menu); | ||
268 | gtk_widget_show(info->list_adv_events_menu); | ||
269 | } | ||
270 | |||
271 | static void sync_event_filters(struct shark_info *info) | ||
272 | { | ||
273 | info->sync_event_filters = 1; | ||
274 | gtk_menu_item_set_label(GTK_MENU_ITEM(info->events_sync_menu), | ||
275 | "Unsync Graph and List Event Filters"); | ||
276 | gtk_menu_item_set_label(GTK_MENU_ITEM(info->graph_events_menu), | ||
277 | "events"); | ||
278 | gtk_menu_item_set_label(GTK_MENU_ITEM(info->graph_adv_events_menu), | ||
279 | "advanced events"); | ||
280 | gtk_widget_hide(info->list_events_menu); | ||
281 | gtk_widget_hide(info->list_adv_events_menu); | ||
282 | } | ||
283 | |||
137 | static void | 284 | static void |
138 | /* Callback for the clicked signal of the Load button */ | 285 | /* Callback for the clicked signal of the Load button */ |
139 | load_clicked (gpointer data) | 286 | load_clicked (gpointer data) |
140 | { | 287 | { |
141 | struct shark_info *info = data; | 288 | struct shark_info *info = data; |
142 | struct tracecmd_input *handle; | 289 | struct tracecmd_input *handle; |
143 | GtkWidget *dialog; | ||
144 | gchar *filename; | 290 | gchar *filename; |
145 | 291 | ||
146 | dialog = gtk_file_chooser_dialog_new("Load File", | 292 | filename = trace_get_file_dialog("Load File"); |
147 | NULL, | 293 | if (!filename) |
148 | GTK_FILE_CHOOSER_ACTION_OPEN, | 294 | return; |
149 | GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | 295 | |
150 | GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, | 296 | handle = tracecmd_open(filename); |
151 | NULL); | 297 | if (handle) { |
152 | if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { | 298 | tracecmd_close(info->handle); |
153 | filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); | 299 | info->handle = handle; |
154 | handle = tracecmd_open(filename); | 300 | trace_graph_load_handle(info->ginfo, handle); |
155 | if (handle) { | 301 | trace_view_reload(info->treeview, handle, info->spin); |
156 | tracecmd_close(info->handle); | 302 | update_title(info->window, filename); |
157 | info->handle = handle; | ||
158 | trace_graph_load_handle(info->ginfo, handle); | ||
159 | trace_view_reload(info->treeview, handle, info->spin); | ||
160 | update_title(info->window, filename); | ||
161 | } | ||
162 | g_free(filename); | ||
163 | } | 303 | } |
164 | gtk_widget_destroy(dialog); | 304 | g_free(filename); |
305 | } | ||
306 | |||
307 | /* Callback for the clicked signal of the Load Filters button */ | ||
308 | static void | ||
309 | load_filters_clicked (gpointer data) | ||
310 | { | ||
311 | struct shark_info *info = data; | ||
312 | struct graph_info *ginfo = info->ginfo; | ||
313 | GtkTreeView *trace_tree = GTK_TREE_VIEW(info->treeview); | ||
314 | GtkTreeModel *model; | ||
315 | TraceViewStore *store; | ||
316 | struct tracecmd_xml_handle *handle; | ||
317 | struct filter_task *task_filter; | ||
318 | struct filter_task *hide_tasks; | ||
319 | struct event_filter *event_filter; | ||
320 | gchar *filename; | ||
321 | int ret; | ||
322 | |||
323 | filename = trace_get_file_dialog("Load Filters"); | ||
324 | if (!filename) | ||
325 | return; | ||
326 | |||
327 | handle = tracecmd_xml_open(filename); | ||
328 | if (!handle) { | ||
329 | warning("Could not open %s", filename); | ||
330 | goto out; | ||
331 | } | ||
332 | |||
333 | /* Unsync the list and graph filters */ | ||
334 | if (info->sync_task_filters) | ||
335 | unsync_task_filters(info); | ||
336 | if (info->sync_event_filters) | ||
337 | unsync_event_filters(info); | ||
338 | |||
339 | ret = tracecmd_xml_system_exists(handle, | ||
340 | "GraphTaskFilter"); | ||
341 | if (ret) { | ||
342 | filter_task_clear(ginfo->task_filter); | ||
343 | filter_task_clear(ginfo->hide_tasks); | ||
344 | |||
345 | trace_filter_load_filters(handle, | ||
346 | "GraphTaskFilter", | ||
347 | ginfo->task_filter, | ||
348 | ginfo->hide_tasks); | ||
349 | trace_graph_refresh_filters(ginfo); | ||
350 | } | ||
351 | |||
352 | ret = tracecmd_xml_system_exists(handle, | ||
353 | "ListTaskFilter"); | ||
354 | if (ret) { | ||
355 | task_filter = info->list_task_filter; | ||
356 | hide_tasks = info->list_hide_tasks; | ||
357 | filter_task_clear(task_filter); | ||
358 | filter_task_clear(hide_tasks); | ||
359 | |||
360 | trace_filter_load_filters(handle, | ||
361 | "ListTaskFilter", | ||
362 | task_filter, | ||
363 | hide_tasks); | ||
364 | update_tree_view_filters(info, task_filter, hide_tasks); | ||
365 | } | ||
366 | |||
367 | trace_graph_load_filters(ginfo, handle); | ||
368 | ret = trace_view_load_filters(handle, trace_tree); | ||
369 | |||
370 | tracecmd_xml_close(handle); | ||
371 | |||
372 | /* | ||
373 | * If the events or tasks filters are the same for both | ||
374 | * the list and graph, then sync them back. | ||
375 | */ | ||
376 | if (filter_task_compare(ginfo->task_filter, | ||
377 | info->list_task_filter) && | ||
378 | filter_task_compare(ginfo->hide_tasks, | ||
379 | info->list_hide_tasks)) | ||
380 | sync_task_filters(info); | ||
381 | |||
382 | model = gtk_tree_view_get_model(trace_tree); | ||
383 | if (!model) | ||
384 | goto out; | ||
385 | |||
386 | store = TRACE_VIEW_STORE(model); | ||
387 | event_filter = trace_view_store_get_event_filter(store); | ||
388 | |||
389 | if (pevent_filter_compare(event_filter, ginfo->event_filter)) | ||
390 | sync_event_filters(info); | ||
391 | |||
392 | out: | ||
393 | g_free(filename); | ||
394 | |||
395 | } | ||
396 | |||
397 | /* Callback for the clicked signal of the Save Filters button */ | ||
398 | static void | ||
399 | save_filters_clicked (gpointer data) | ||
400 | { | ||
401 | struct shark_info *info = data; | ||
402 | struct graph_info *ginfo = info->ginfo; | ||
403 | struct tracecmd_xml_handle *handle; | ||
404 | GtkTreeView *trace_tree = GTK_TREE_VIEW(info->treeview); | ||
405 | struct filter_task *task_filter; | ||
406 | struct filter_task *hide_tasks; | ||
407 | gchar *filename; | ||
408 | |||
409 | filename = trace_get_file_dialog("Save Filters"); | ||
410 | if (!filename) | ||
411 | return; | ||
412 | |||
413 | handle = tracecmd_xml_create(filename, VERSION_STRING); | ||
414 | if (!handle) | ||
415 | warning("Could not create %s", filename); | ||
416 | g_free(filename); | ||
417 | |||
418 | trace_view_save_filters(handle, trace_tree); | ||
419 | trace_graph_save_filters(ginfo, handle); | ||
420 | |||
421 | trace_filter_save_filters(handle, | ||
422 | "GraphTaskFilter", | ||
423 | ginfo->task_filter, | ||
424 | ginfo->hide_tasks); | ||
425 | |||
426 | if (info->sync_task_filters) { | ||
427 | task_filter = ginfo->task_filter; | ||
428 | hide_tasks = ginfo->hide_tasks; | ||
429 | } else { | ||
430 | task_filter = info->list_task_filter; | ||
431 | hide_tasks = info->list_hide_tasks; | ||
432 | } | ||
433 | |||
434 | trace_filter_save_filters(handle, | ||
435 | "ListTaskFilter", | ||
436 | task_filter, hide_tasks); | ||
437 | |||
438 | tracecmd_xml_close(handle); | ||
165 | } | 439 | } |
166 | 440 | ||
167 | /* Callback for the clicked signal of the Exit button */ | 441 | /* Callback for the clicked signal of the Exit button */ |
@@ -187,16 +461,28 @@ delete_event (GtkWidget *widget, GdkEvent *event, gpointer data) | |||
187 | return TRUE; | 461 | return TRUE; |
188 | } | 462 | } |
189 | 463 | ||
190 | /* Callback for the clicked signal of the Events filter button */ | 464 | /* Callback for the clicked signal of the tasks sync filter button */ |
191 | static void | 465 | static void |
192 | list_events_clicked (gpointer data) | 466 | sync_task_filter_clicked (GtkWidget *subitem, gpointer data) |
193 | { | 467 | { |
194 | struct shark_info *info = data; | 468 | struct shark_info *info = data; |
195 | struct event_filter *event_filter; | 469 | struct filter_task *task_filter; |
470 | struct filter_task *hide_tasks; | ||
196 | GtkTreeView *trace_tree = GTK_TREE_VIEW(info->treeview); | 471 | GtkTreeView *trace_tree = GTK_TREE_VIEW(info->treeview); |
197 | GtkTreeModel *model; | 472 | GtkTreeModel *model; |
198 | TraceViewStore *store; | 473 | TraceViewStore *store; |
199 | gboolean all_events; | 474 | gboolean keep; |
475 | gchar *selections[] = { "Sync List Filter with Graph Filter", | ||
476 | "Sync Graph Filter with List Filter", | ||
477 | NULL }; | ||
478 | int result; | ||
479 | |||
480 | if (info->sync_task_filters) { | ||
481 | /* Separate the List and Graph filters */ | ||
482 | |||
483 | unsync_task_filters(info); | ||
484 | return; | ||
485 | } | ||
200 | 486 | ||
201 | model = gtk_tree_view_get_model(trace_tree); | 487 | model = gtk_tree_view_get_model(trace_tree); |
202 | if (!model) | 488 | if (!model) |
@@ -204,40 +490,204 @@ list_events_clicked (gpointer data) | |||
204 | 490 | ||
205 | store = TRACE_VIEW_STORE(model); | 491 | store = TRACE_VIEW_STORE(model); |
206 | 492 | ||
207 | all_events = trace_view_store_get_all_events_enabled(store); | 493 | /* If they are already equal, then just perminently sync them */ |
208 | event_filter = trace_view_store_get_event_filter(store); | 494 | if (filter_task_compare(info->ginfo->task_filter, |
495 | info->list_task_filter) && | ||
496 | filter_task_compare(info->ginfo->hide_tasks, | ||
497 | info->list_hide_tasks)) | ||
498 | result = 2; | ||
209 | 499 | ||
210 | trace_filter_event_filter_dialog(store->handle, event_filter, | 500 | else |
211 | all_events, | 501 | /* Ask user which way to sync */ |
212 | trace_view_event_filter_callback, | 502 | result = trace_sync_select_menu("Sync Task Filters", |
213 | info->treeview); | 503 | selections, &keep); |
504 | |||
505 | switch (result) { | ||
506 | case 0: | ||
507 | /* Sync List Filter with Graph Filter */ | ||
508 | filter_task_hash_free(info->list_task_filter); | ||
509 | filter_task_hash_free(info->list_hide_tasks); | ||
510 | |||
511 | info->list_task_filter = NULL; | ||
512 | info->list_hide_tasks = NULL; | ||
513 | |||
514 | task_filter = info->ginfo->task_filter; | ||
515 | hide_tasks = info->ginfo->hide_tasks; | ||
516 | |||
517 | if (!keep) { | ||
518 | info->list_task_filter = filter_task_hash_copy(task_filter); | ||
519 | info->list_hide_tasks = filter_task_hash_copy(hide_tasks); | ||
520 | } | ||
521 | |||
522 | update_tree_view_filters(info, task_filter, hide_tasks); | ||
523 | |||
524 | break; | ||
525 | case 1: | ||
526 | /* Sync Graph Filter with List Filter */ | ||
527 | trace_graph_update_filters(info->ginfo, | ||
528 | info->list_task_filter, | ||
529 | info->list_hide_tasks); | ||
530 | |||
531 | if (keep) { | ||
532 | filter_task_hash_free(info->list_task_filter); | ||
533 | filter_task_hash_free(info->list_hide_tasks); | ||
534 | |||
535 | info->list_task_filter = NULL; | ||
536 | info->list_hide_tasks = NULL; | ||
537 | } | ||
538 | break; | ||
539 | case 2: | ||
540 | keep = 1; | ||
541 | break; | ||
542 | default: | ||
543 | keep = 0; | ||
544 | } | ||
545 | |||
546 | if (keep) | ||
547 | sync_task_filters(info); | ||
214 | } | 548 | } |
215 | 549 | ||
550 | /* Callback for the clicked signal of the events sync filter button */ | ||
216 | static void | 551 | static void |
217 | graph_events_clicked (gpointer data) | 552 | sync_events_filter_clicked (GtkWidget *subitem, gpointer data) |
218 | { | 553 | { |
219 | struct shark_info *info = data; | 554 | struct shark_info *info = data; |
220 | struct graph_info *ginfo = info->ginfo; | 555 | struct graph_info *ginfo = info->ginfo; |
556 | struct event_filter *event_filter; | ||
557 | GtkTreeView *trace_tree = GTK_TREE_VIEW(info->treeview); | ||
558 | GtkTreeModel *model; | ||
559 | TraceViewStore *store; | ||
560 | gboolean keep; | ||
221 | gboolean all_events; | 561 | gboolean all_events; |
562 | gchar *selections[] = { "Sync List Filter with Graph Filter", | ||
563 | "Sync Graph Filter with List Filter", | ||
564 | NULL }; | ||
565 | int result; | ||
566 | |||
567 | if (info->sync_event_filters) { | ||
568 | /* Separate the List and Graph filters */ | ||
569 | unsync_event_filters(info); | ||
570 | return; | ||
571 | } | ||
222 | 572 | ||
223 | all_events = ginfo->all_events; | 573 | model = gtk_tree_view_get_model(trace_tree); |
574 | if (!model) | ||
575 | return; | ||
224 | 576 | ||
225 | trace_filter_event_filter_dialog(info->handle, | 577 | store = TRACE_VIEW_STORE(model); |
226 | ginfo->event_filter, | 578 | event_filter = trace_view_store_get_event_filter(store); |
227 | all_events, | 579 | |
228 | trace_graph_event_filter_callback, | 580 | /* If they are already equal, then just perminently sync them */ |
229 | ginfo); | 581 | if (pevent_filter_compare(event_filter, ginfo->event_filter)) |
582 | result = 2; | ||
583 | else | ||
584 | /* Ask user which way to sync */ | ||
585 | result = trace_sync_select_menu("Sync Event Filters", | ||
586 | selections, &keep); | ||
587 | |||
588 | switch (result) { | ||
589 | case 0: | ||
590 | /* Sync List Filter with Graph Filter */ | ||
591 | all_events = ginfo->all_events; | ||
592 | |||
593 | trace_view_copy_filter(info->treeview, all_events, | ||
594 | ginfo->event_filter); | ||
595 | break; | ||
596 | case 1: | ||
597 | /* Sync Graph Filter with List Filter */ | ||
598 | all_events = trace_view_store_get_all_events_enabled(store); | ||
599 | |||
600 | trace_graph_copy_filter(info->ginfo, all_events, | ||
601 | event_filter); | ||
602 | break; | ||
603 | case 2: | ||
604 | keep = 1; | ||
605 | break; | ||
606 | default: | ||
607 | keep = 0; | ||
608 | } | ||
609 | |||
610 | if (keep) | ||
611 | sync_event_filters(info); | ||
230 | } | 612 | } |
231 | 613 | ||
232 | /* Callback for the clicked signal of the List advanced filter button */ | 614 | static void filter_list_enable_clicked (gpointer data); |
615 | |||
233 | static void | 616 | static void |
234 | adv_list_filter_clicked (gpointer data) | 617 | __update_list_task_filter_callback(struct shark_info *info, |
618 | gboolean accept, | ||
619 | gint *selected, | ||
620 | struct filter_task *task_filter) | ||
621 | { | ||
622 | GtkTreeView *trace_tree = GTK_TREE_VIEW(info->treeview); | ||
623 | GtkTreeModel *model; | ||
624 | TraceViewStore *store; | ||
625 | int i; | ||
626 | |||
627 | if (!accept) | ||
628 | return; | ||
629 | |||
630 | model = gtk_tree_view_get_model(trace_tree); | ||
631 | if (!model) | ||
632 | return; | ||
633 | |||
634 | store = TRACE_VIEW_STORE(model); | ||
635 | |||
636 | filter_task_clear(task_filter); | ||
637 | |||
638 | if (selected) { | ||
639 | for (i = 0; selected[i] >= 0; i++) | ||
640 | filter_task_add_pid(task_filter, selected[i]); | ||
641 | } | ||
642 | |||
643 | update_tree_view_filters(info, info->list_task_filter, info->list_hide_tasks); | ||
644 | |||
645 | /* | ||
646 | * The menu filters always enable the filters. | ||
647 | */ | ||
648 | if (info->list_filter_available && !info->list_filter_enabled) | ||
649 | filter_list_enable_clicked(info); | ||
650 | } | ||
651 | |||
652 | static void | ||
653 | update_list_task_filter_callback(gboolean accept, | ||
654 | gint *selected, | ||
655 | gint *non_select, | ||
656 | gpointer data) | ||
235 | { | 657 | { |
236 | struct shark_info *info = data; | 658 | struct shark_info *info = data; |
237 | struct event_filter *event_filter; | 659 | |
660 | __update_list_task_filter_callback(info, accept, selected, | ||
661 | info->list_task_filter); | ||
662 | } | ||
663 | |||
664 | static void | ||
665 | update_list_hide_task_filter_callback(gboolean accept, | ||
666 | gint *selected, | ||
667 | gint *non_select, | ||
668 | gpointer data) | ||
669 | { | ||
670 | struct shark_info *info = data; | ||
671 | |||
672 | __update_list_task_filter_callback(info, accept, selected, | ||
673 | info->list_hide_tasks); | ||
674 | } | ||
675 | |||
676 | /* Callback for the clicked signal of the List Tasks filter button */ | ||
677 | static void | ||
678 | __list_tasks_clicked (struct shark_info *info, | ||
679 | struct filter_task *task_filter, | ||
680 | trace_task_cb_func func) | ||
681 | { | ||
238 | GtkTreeView *trace_tree = GTK_TREE_VIEW(info->treeview); | 682 | GtkTreeView *trace_tree = GTK_TREE_VIEW(info->treeview); |
683 | struct graph_info *ginfo = info->ginfo; | ||
239 | GtkTreeModel *model; | 684 | GtkTreeModel *model; |
240 | TraceViewStore *store; | 685 | TraceViewStore *store; |
686 | gint *selected; | ||
687 | gint *tasks; | ||
688 | |||
689 | if (!ginfo->handle) | ||
690 | return; | ||
241 | 691 | ||
242 | model = gtk_tree_view_get_model(trace_tree); | 692 | model = gtk_tree_view_get_model(trace_tree); |
243 | if (!model) | 693 | if (!model) |
@@ -245,25 +695,137 @@ adv_list_filter_clicked (gpointer data) | |||
245 | 695 | ||
246 | store = TRACE_VIEW_STORE(model); | 696 | store = TRACE_VIEW_STORE(model); |
247 | 697 | ||
248 | event_filter = trace_view_store_get_event_filter(store); | 698 | tasks = trace_graph_task_list(ginfo); |
699 | selected = filter_task_pids(task_filter); | ||
249 | 700 | ||
250 | trace_adv_filter_dialog(store->handle, event_filter, | 701 | trace_task_dialog(info->handle, tasks, selected, func, info); |
251 | trace_view_adv_filter_callback, trace_tree); | 702 | |
703 | free(tasks); | ||
704 | free(selected); | ||
252 | } | 705 | } |
253 | 706 | ||
254 | /* Callback for the clicked signal of the Graph advanced filter button */ | ||
255 | static void | 707 | static void |
256 | adv_graph_filter_clicked (gpointer data) | 708 | list_tasks_clicked (gpointer data) |
257 | { | 709 | { |
258 | struct shark_info *info = data; | 710 | struct shark_info *info = data; |
711 | |||
712 | __list_tasks_clicked(info, info->list_task_filter, | ||
713 | update_list_task_filter_callback); | ||
714 | } | ||
715 | |||
716 | static void | ||
717 | list_hide_tasks_clicked (gpointer data) | ||
718 | { | ||
719 | struct shark_info *info = data; | ||
720 | |||
721 | __list_tasks_clicked(info, info->list_hide_tasks, | ||
722 | update_list_hide_task_filter_callback); | ||
723 | } | ||
724 | |||
725 | static void | ||
726 | __update_graph_task_filter_callback(struct shark_info *info, | ||
727 | gboolean accept, | ||
728 | gint *selected, | ||
729 | struct filter_task *task_filter) | ||
730 | { | ||
259 | struct graph_info *ginfo = info->ginfo; | 731 | struct graph_info *ginfo = info->ginfo; |
732 | int i; | ||
260 | 733 | ||
261 | trace_adv_filter_dialog(ginfo->handle, ginfo->event_filter, | 734 | if (!accept) |
262 | trace_graph_adv_filter_callback, ginfo); | 735 | return; |
736 | |||
737 | filter_task_clear(task_filter); | ||
738 | |||
739 | if (selected) { | ||
740 | for (i = 0; selected[i] >= 0; i++) | ||
741 | filter_task_add_pid(task_filter, selected[i]); | ||
742 | } | ||
743 | |||
744 | trace_graph_refresh_filters(ginfo); | ||
745 | |||
746 | /* | ||
747 | * The menu filters always enable the filters. | ||
748 | */ | ||
749 | if (ginfo->filter_available) { | ||
750 | if (!ginfo->filter_enabled) | ||
751 | trace_graph_filter_toggle(info->ginfo); | ||
752 | |||
753 | if (info->sync_task_filters && !info->list_filter_enabled) | ||
754 | filter_list_enable_clicked(info); | ||
755 | } | ||
263 | } | 756 | } |
264 | 757 | ||
265 | static void | 758 | static void |
266 | sync_graph_events_to_list_clicked (gpointer data) | 759 | update_graph_task_filter_callback(gboolean accept, |
760 | gint *selected, | ||
761 | gint *non_select, | ||
762 | gpointer data) | ||
763 | { | ||
764 | struct shark_info *info = data; | ||
765 | struct graph_info *ginfo = info->ginfo; | ||
766 | |||
767 | __update_graph_task_filter_callback(info, accept, selected, | ||
768 | ginfo->task_filter); | ||
769 | } | ||
770 | |||
771 | static void | ||
772 | update_graph_hide_task_filter_callback(gboolean accept, | ||
773 | gint *selected, | ||
774 | gint *non_select, | ||
775 | gpointer data) | ||
776 | { | ||
777 | struct shark_info *info = data; | ||
778 | struct graph_info *ginfo = info->ginfo; | ||
779 | |||
780 | __update_graph_task_filter_callback(info, accept, selected, | ||
781 | ginfo->hide_tasks); | ||
782 | } | ||
783 | |||
784 | /* Callback for the clicked signal of the Tasks filter button */ | ||
785 | static void | ||
786 | __graph_tasks_clicked (struct shark_info *info, | ||
787 | struct filter_task *task_filter, | ||
788 | trace_task_cb_func func) | ||
789 | { | ||
790 | struct graph_info *ginfo = info->ginfo; | ||
791 | gint *selected; | ||
792 | gint *tasks; | ||
793 | |||
794 | if (!ginfo->handle) | ||
795 | return; | ||
796 | |||
797 | tasks = trace_graph_task_list(ginfo); | ||
798 | selected = filter_task_pids(task_filter); | ||
799 | |||
800 | trace_task_dialog(ginfo->handle, tasks, selected, func, info); | ||
801 | |||
802 | free(tasks); | ||
803 | free(selected); | ||
804 | } | ||
805 | |||
806 | static void | ||
807 | graph_tasks_clicked (gpointer data) | ||
808 | { | ||
809 | struct shark_info *info = data; | ||
810 | struct graph_info *ginfo = info->ginfo; | ||
811 | |||
812 | __graph_tasks_clicked(info, ginfo->task_filter, | ||
813 | update_graph_task_filter_callback); | ||
814 | } | ||
815 | |||
816 | static void | ||
817 | graph_hide_tasks_clicked (gpointer data) | ||
818 | { | ||
819 | struct shark_info *info = data; | ||
820 | struct graph_info *ginfo = info->ginfo; | ||
821 | |||
822 | __graph_tasks_clicked(info, ginfo->hide_tasks, | ||
823 | update_graph_hide_task_filter_callback); | ||
824 | } | ||
825 | |||
826 | /* Callback for the clicked signal of the Events filter button */ | ||
827 | static void | ||
828 | list_events_clicked (gpointer data) | ||
267 | { | 829 | { |
268 | struct shark_info *info = data; | 830 | struct shark_info *info = data; |
269 | struct event_filter *event_filter; | 831 | struct event_filter *event_filter; |
@@ -281,13 +843,36 @@ sync_graph_events_to_list_clicked (gpointer data) | |||
281 | all_events = trace_view_store_get_all_events_enabled(store); | 843 | all_events = trace_view_store_get_all_events_enabled(store); |
282 | event_filter = trace_view_store_get_event_filter(store); | 844 | event_filter = trace_view_store_get_event_filter(store); |
283 | 845 | ||
284 | trace_graph_copy_filter(info->ginfo, all_events, | 846 | /* |
285 | event_filter); | 847 | * This menu is not available when in sync, so we |
848 | * can call the treeview callback directly. | ||
849 | */ | ||
850 | trace_filter_event_filter_dialog(store->handle, event_filter, | ||
851 | all_events, | ||
852 | trace_view_event_filter_callback, | ||
853 | info->treeview); | ||
286 | } | 854 | } |
287 | 855 | ||
856 | static void | ||
857 | graph_event_filter_callback(gboolean accept, | ||
858 | gboolean all_events, | ||
859 | gchar **systems, | ||
860 | gint *events, | ||
861 | gpointer data) | ||
862 | { | ||
863 | struct shark_info *info = data; | ||
864 | |||
865 | trace_graph_event_filter_callback(accept, all_events, | ||
866 | systems, events, | ||
867 | info->ginfo); | ||
868 | |||
869 | if (info->sync_event_filters) | ||
870 | trace_view_event_filter_callback(accept, all_events, systems, | ||
871 | events, info->treeview); | ||
872 | } | ||
288 | 873 | ||
289 | static void | 874 | static void |
290 | sync_list_events_to_graph_clicked (gpointer data) | 875 | graph_events_clicked (gpointer data) |
291 | { | 876 | { |
292 | struct shark_info *info = data; | 877 | struct shark_info *info = data; |
293 | struct graph_info *ginfo = info->ginfo; | 878 | struct graph_info *ginfo = info->ginfo; |
@@ -295,8 +880,64 @@ sync_list_events_to_graph_clicked (gpointer data) | |||
295 | 880 | ||
296 | all_events = ginfo->all_events; | 881 | all_events = ginfo->all_events; |
297 | 882 | ||
298 | trace_view_copy_filter(info->treeview, all_events, | 883 | trace_filter_event_filter_dialog(info->handle, |
299 | ginfo->event_filter); | 884 | ginfo->event_filter, |
885 | all_events, | ||
886 | graph_event_filter_callback, | ||
887 | info); | ||
888 | } | ||
889 | |||
890 | /* Callback for the clicked signal of the List advanced filter button */ | ||
891 | static void | ||
892 | adv_list_filter_clicked (gpointer data) | ||
893 | { | ||
894 | struct shark_info *info = data; | ||
895 | struct event_filter *event_filter; | ||
896 | GtkTreeView *trace_tree = GTK_TREE_VIEW(info->treeview); | ||
897 | GtkTreeModel *model; | ||
898 | TraceViewStore *store; | ||
899 | |||
900 | model = gtk_tree_view_get_model(trace_tree); | ||
901 | if (!model) | ||
902 | return; | ||
903 | |||
904 | store = TRACE_VIEW_STORE(model); | ||
905 | |||
906 | event_filter = trace_view_store_get_event_filter(store); | ||
907 | |||
908 | /* | ||
909 | * This menu is not available when in sync, so we | ||
910 | * can call the treeview callback directly. | ||
911 | */ | ||
912 | trace_adv_filter_dialog(store->handle, event_filter, | ||
913 | trace_view_adv_filter_callback, trace_tree); | ||
914 | } | ||
915 | |||
916 | static void | ||
917 | graph_adv_filter_callback(gboolean accept, | ||
918 | const gchar *text, | ||
919 | gint *event_ids, | ||
920 | gpointer data) | ||
921 | { | ||
922 | struct shark_info *info = data; | ||
923 | struct graph_info *ginfo = info->ginfo; | ||
924 | |||
925 | trace_graph_adv_filter_callback(accept, text, event_ids, ginfo); | ||
926 | |||
927 | if (info->sync_event_filters) | ||
928 | trace_view_adv_filter_callback(accept, text, event_ids, | ||
929 | info->treeview); | ||
930 | } | ||
931 | |||
932 | /* Callback for the clicked signal of the Graph advanced filter button */ | ||
933 | static void | ||
934 | adv_graph_filter_clicked (gpointer data) | ||
935 | { | ||
936 | struct shark_info *info = data; | ||
937 | struct graph_info *ginfo = info->ginfo; | ||
938 | |||
939 | trace_adv_filter_dialog(ginfo->handle, ginfo->event_filter, | ||
940 | graph_adv_filter_callback, info); | ||
300 | } | 941 | } |
301 | 942 | ||
302 | /* Callback for the clicked signal of the CPUs filter button */ | 943 | /* Callback for the clicked signal of the CPUs filter button */ |
@@ -362,6 +1003,34 @@ plot_tasks_clicked (gpointer data) | |||
362 | free(selected); | 1003 | free(selected); |
363 | } | 1004 | } |
364 | 1005 | ||
1006 | /* Callback for the clicked signal of the help contents button */ | ||
1007 | static void | ||
1008 | help_content_clicked (gpointer data) | ||
1009 | { | ||
1010 | struct shark_info *info = data; | ||
1011 | GError *error = NULL; | ||
1012 | gchar *link; | ||
1013 | |||
1014 | link = "file://" __stringify(HELP_DIR) "/index.html"; | ||
1015 | |||
1016 | trace_show_help(info->window, link, &error); | ||
1017 | } | ||
1018 | |||
1019 | |||
1020 | /* Callback for the clicked signal of the help about button */ | ||
1021 | static void | ||
1022 | help_about_clicked (gpointer data) | ||
1023 | { | ||
1024 | struct shark_info *info = data; | ||
1025 | |||
1026 | trace_dialog(GTK_WINDOW(info->window), TRACE_GUI_INFO, | ||
1027 | "KernelShark\n\n" | ||
1028 | "version %s\n\n" | ||
1029 | "Copyright (C) 2009, 2010 Red Hat Inc\n\n" | ||
1030 | " Author: Steven Rostedt <srostedt@redhat.com>", | ||
1031 | VERSION_STRING); | ||
1032 | } | ||
1033 | |||
365 | static void graph_follows_tree(struct shark_info *info, | 1034 | static void graph_follows_tree(struct shark_info *info, |
366 | GtkTreeView *treeview, | 1035 | GtkTreeView *treeview, |
367 | GtkTreePath *path) | 1036 | GtkTreePath *path) |
@@ -427,26 +1096,70 @@ static void | |||
427 | filter_list_enable_clicked (gpointer data) | 1096 | filter_list_enable_clicked (gpointer data) |
428 | { | 1097 | { |
429 | struct shark_info *info = data; | 1098 | struct shark_info *info = data; |
1099 | struct filter_task *task_filter; | ||
1100 | struct filter_task *hide_tasks; | ||
430 | 1101 | ||
431 | info->list_filter_enabled ^= 1; | 1102 | info->list_filter_enabled ^= 1; |
432 | 1103 | ||
1104 | if (info->sync_task_filters) { | ||
1105 | task_filter = info->ginfo->task_filter; | ||
1106 | hide_tasks = info->ginfo->hide_tasks; | ||
1107 | } else { | ||
1108 | task_filter = info->list_task_filter; | ||
1109 | hide_tasks = info->list_hide_tasks; | ||
1110 | } | ||
1111 | |||
433 | if (info->list_filter_enabled) | 1112 | if (info->list_filter_enabled) |
434 | trace_view_update_filters(info->treeview, | 1113 | trace_view_update_filters(info->treeview, |
435 | info->ginfo->task_filter, | 1114 | task_filter, hide_tasks); |
436 | info->ginfo->hide_tasks); | ||
437 | else | 1115 | else |
438 | trace_view_update_filters(info->treeview, NULL, NULL); | 1116 | trace_view_update_filters(info->treeview, NULL, NULL); |
439 | } | 1117 | } |
440 | 1118 | ||
441 | static void | 1119 | static void |
1120 | filter_update_list_filter(struct shark_info *info, | ||
1121 | struct filter_task *filter, | ||
1122 | struct filter_task *other_filter) | ||
1123 | { | ||
1124 | struct filter_task_item *task; | ||
1125 | int pid = info->selected_task; | ||
1126 | |||
1127 | task = filter_task_find_pid(filter, pid); | ||
1128 | if (task) { | ||
1129 | filter_task_remove_pid(filter, pid); | ||
1130 | if (!filter_task_count(filter) && | ||
1131 | !filter_task_count(other_filter)) { | ||
1132 | info->list_filter_enabled = 0; | ||
1133 | info->list_filter_available = 0; | ||
1134 | } | ||
1135 | } else { | ||
1136 | filter_task_add_pid(filter, pid); | ||
1137 | info->list_filter_available = 1; | ||
1138 | } | ||
1139 | } | ||
1140 | |||
1141 | static void | ||
442 | filter_add_task_clicked (gpointer data) | 1142 | filter_add_task_clicked (gpointer data) |
443 | { | 1143 | { |
444 | struct shark_info *info = data; | 1144 | struct shark_info *info = data; |
1145 | int pid = info->selected_task; | ||
445 | 1146 | ||
446 | trace_graph_filter_add_remove_task(info->ginfo, info->selected_task); | 1147 | if (info->sync_task_filters) { |
1148 | trace_graph_filter_add_remove_task(info->ginfo, pid); | ||
1149 | return; | ||
1150 | } | ||
447 | 1151 | ||
448 | if (!filter_task_count(info->ginfo->task_filter)) | 1152 | filter_update_list_filter(info, info->list_task_filter, info->list_hide_tasks); |
449 | info->list_filter_enabled = 0; | 1153 | trace_view_update_filters(info->treeview, |
1154 | info->list_task_filter, info->list_hide_tasks); | ||
1155 | } | ||
1156 | |||
1157 | static void | ||
1158 | filter_graph_add_task_clicked (gpointer data) | ||
1159 | { | ||
1160 | struct shark_info *info = data; | ||
1161 | |||
1162 | trace_graph_filter_add_remove_task(info->ginfo, info->selected_task); | ||
450 | } | 1163 | } |
451 | 1164 | ||
452 | static void | 1165 | static void |
@@ -454,11 +1167,22 @@ filter_hide_task_clicked (gpointer data) | |||
454 | { | 1167 | { |
455 | struct shark_info *info = data; | 1168 | struct shark_info *info = data; |
456 | 1169 | ||
457 | trace_graph_filter_hide_show_task(info->ginfo, info->selected_task); | 1170 | if (info->sync_task_filters) { |
1171 | trace_graph_filter_hide_show_task(info->ginfo, info->selected_task); | ||
1172 | return; | ||
1173 | } | ||
458 | 1174 | ||
459 | if (!filter_task_count(info->ginfo->task_filter) && | 1175 | filter_update_list_filter(info, info->list_hide_tasks, info->list_task_filter); |
460 | !filter_task_count(info->ginfo->hide_tasks)) | 1176 | trace_view_update_filters(info->treeview, |
461 | info->list_filter_enabled = 0; | 1177 | info->list_task_filter, info->list_hide_tasks); |
1178 | } | ||
1179 | |||
1180 | static void | ||
1181 | filter_graph_hide_task_clicked (gpointer data) | ||
1182 | { | ||
1183 | struct shark_info *info = data; | ||
1184 | |||
1185 | trace_graph_filter_hide_show_task(info->ginfo, info->selected_task); | ||
462 | } | 1186 | } |
463 | 1187 | ||
464 | static void | 1188 | static void |
@@ -466,11 +1190,27 @@ filter_clear_tasks_clicked (gpointer data) | |||
466 | { | 1190 | { |
467 | struct shark_info *info = data; | 1191 | struct shark_info *info = data; |
468 | 1192 | ||
469 | trace_graph_clear_tasks(info->ginfo); | 1193 | if (info->sync_task_filters) { |
1194 | trace_graph_clear_tasks(info->ginfo); | ||
1195 | return; | ||
1196 | } | ||
1197 | |||
1198 | filter_task_clear(info->list_task_filter); | ||
1199 | filter_task_clear(info->list_hide_tasks); | ||
1200 | trace_view_update_filters(info->treeview, NULL, NULL); | ||
470 | 1201 | ||
1202 | info->list_filter_available = 0; | ||
471 | info->list_filter_enabled = 0; | 1203 | info->list_filter_enabled = 0; |
472 | } | 1204 | } |
473 | 1205 | ||
1206 | static void | ||
1207 | filter_graph_clear_tasks_clicked (gpointer data) | ||
1208 | { | ||
1209 | struct shark_info *info = data; | ||
1210 | |||
1211 | trace_graph_clear_tasks(info->ginfo); | ||
1212 | } | ||
1213 | |||
474 | static void graph_check_toggle(gpointer data, GtkWidget *widget) | 1214 | static void graph_check_toggle(gpointer data, GtkWidget *widget) |
475 | { | 1215 | { |
476 | struct shark_info *info = data; | 1216 | struct shark_info *info = data; |
@@ -478,6 +1218,17 @@ static void graph_check_toggle(gpointer data, GtkWidget *widget) | |||
478 | info->graph_follows = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); | 1218 | info->graph_follows = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); |
479 | } | 1219 | } |
480 | 1220 | ||
1221 | static void set_menu_label(GtkWidget *menu, const char *comm, int pid, | ||
1222 | const char *fmt) | ||
1223 | { | ||
1224 | int len = strlen(comm) + strlen(fmt) + 50; | ||
1225 | char text[len]; | ||
1226 | |||
1227 | snprintf(text, len, fmt, comm, pid); | ||
1228 | |||
1229 | gtk_menu_item_set_label(GTK_MENU_ITEM(menu), text); | ||
1230 | } | ||
1231 | |||
481 | static gboolean | 1232 | static gboolean |
482 | do_tree_popup(GtkWidget *widget, GdkEventButton *event, gpointer data) | 1233 | do_tree_popup(GtkWidget *widget, GdkEventButton *event, gpointer data) |
483 | { | 1234 | { |
@@ -489,11 +1240,13 @@ do_tree_popup(GtkWidget *widget, GdkEventButton *event, gpointer data) | |||
489 | static GtkWidget *menu_filter_add_task; | 1240 | static GtkWidget *menu_filter_add_task; |
490 | static GtkWidget *menu_filter_hide_task; | 1241 | static GtkWidget *menu_filter_hide_task; |
491 | static GtkWidget *menu_filter_clear_tasks; | 1242 | static GtkWidget *menu_filter_clear_tasks; |
1243 | static GtkWidget *menu_filter_graph_add_task; | ||
1244 | static GtkWidget *menu_filter_graph_hide_task; | ||
1245 | static GtkWidget *menu_filter_graph_clear_tasks; | ||
492 | struct record *record; | 1246 | struct record *record; |
493 | TraceViewRecord *vrec; | 1247 | TraceViewRecord *vrec; |
494 | GtkTreeModel *model; | 1248 | GtkTreeModel *model; |
495 | const char *comm; | 1249 | const char *comm; |
496 | gchar *text; | ||
497 | gint pid; | 1250 | gint pid; |
498 | gint len; | 1251 | gint len; |
499 | guint64 offset; | 1252 | guint64 offset; |
@@ -502,7 +1255,7 @@ do_tree_popup(GtkWidget *widget, GdkEventButton *event, gpointer data) | |||
502 | 1255 | ||
503 | if (!menu) { | 1256 | if (!menu) { |
504 | menu = gtk_menu_new(); | 1257 | menu = gtk_menu_new(); |
505 | menu_filter_graph_enable = gtk_menu_item_new_with_label("Enable Graph Filter"); | 1258 | menu_filter_graph_enable = gtk_menu_item_new_with_label("Enable Graph Task Filter"); |
506 | gtk_widget_show(menu_filter_graph_enable); | 1259 | gtk_widget_show(menu_filter_graph_enable); |
507 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_graph_enable); | 1260 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_graph_enable); |
508 | 1261 | ||
@@ -510,7 +1263,7 @@ do_tree_popup(GtkWidget *widget, GdkEventButton *event, gpointer data) | |||
510 | G_CALLBACK (filter_graph_enable_clicked), | 1263 | G_CALLBACK (filter_graph_enable_clicked), |
511 | data); | 1264 | data); |
512 | 1265 | ||
513 | menu_filter_list_enable = gtk_menu_item_new_with_label("Enable List Filter"); | 1266 | menu_filter_list_enable = gtk_menu_item_new_with_label("Enable List Task Filter"); |
514 | gtk_widget_show(menu_filter_list_enable); | 1267 | gtk_widget_show(menu_filter_list_enable); |
515 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_list_enable); | 1268 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_list_enable); |
516 | 1269 | ||
@@ -526,6 +1279,14 @@ do_tree_popup(GtkWidget *widget, GdkEventButton *event, gpointer data) | |||
526 | G_CALLBACK (filter_add_task_clicked), | 1279 | G_CALLBACK (filter_add_task_clicked), |
527 | data); | 1280 | data); |
528 | 1281 | ||
1282 | menu_filter_graph_add_task = gtk_menu_item_new_with_label("Add Task to Graph"); | ||
1283 | gtk_widget_show(menu_filter_graph_add_task); | ||
1284 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_graph_add_task); | ||
1285 | |||
1286 | g_signal_connect_swapped (G_OBJECT (menu_filter_graph_add_task), "activate", | ||
1287 | G_CALLBACK (filter_graph_add_task_clicked), | ||
1288 | data); | ||
1289 | |||
529 | menu_filter_hide_task = gtk_menu_item_new_with_label("Hide Task"); | 1290 | menu_filter_hide_task = gtk_menu_item_new_with_label("Hide Task"); |
530 | gtk_widget_show(menu_filter_hide_task); | 1291 | gtk_widget_show(menu_filter_hide_task); |
531 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_hide_task); | 1292 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_hide_task); |
@@ -534,6 +1295,14 @@ do_tree_popup(GtkWidget *widget, GdkEventButton *event, gpointer data) | |||
534 | G_CALLBACK (filter_hide_task_clicked), | 1295 | G_CALLBACK (filter_hide_task_clicked), |
535 | data); | 1296 | data); |
536 | 1297 | ||
1298 | menu_filter_graph_hide_task = gtk_menu_item_new_with_label("Hide Task from Graph"); | ||
1299 | gtk_widget_show(menu_filter_graph_hide_task); | ||
1300 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_graph_hide_task); | ||
1301 | |||
1302 | g_signal_connect_swapped (G_OBJECT (menu_filter_graph_hide_task), "activate", | ||
1303 | G_CALLBACK (filter_graph_hide_task_clicked), | ||
1304 | data); | ||
1305 | |||
537 | menu_filter_clear_tasks = gtk_menu_item_new_with_label("Clear Task Filter"); | 1306 | menu_filter_clear_tasks = gtk_menu_item_new_with_label("Clear Task Filter"); |
538 | gtk_widget_show(menu_filter_clear_tasks); | 1307 | gtk_widget_show(menu_filter_clear_tasks); |
539 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_clear_tasks); | 1308 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_clear_tasks); |
@@ -542,6 +1311,15 @@ do_tree_popup(GtkWidget *widget, GdkEventButton *event, gpointer data) | |||
542 | G_CALLBACK (filter_clear_tasks_clicked), | 1311 | G_CALLBACK (filter_clear_tasks_clicked), |
543 | data); | 1312 | data); |
544 | 1313 | ||
1314 | menu_filter_graph_clear_tasks = | ||
1315 | gtk_menu_item_new_with_label("Clear Graph Task Filter"); | ||
1316 | gtk_widget_show(menu_filter_graph_clear_tasks); | ||
1317 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_graph_clear_tasks); | ||
1318 | |||
1319 | g_signal_connect_swapped (G_OBJECT (menu_filter_graph_clear_tasks), "activate", | ||
1320 | G_CALLBACK (filter_graph_clear_tasks_clicked), | ||
1321 | data); | ||
1322 | |||
545 | } | 1323 | } |
546 | 1324 | ||
547 | row = trace_view_get_selected_row(GTK_WIDGET(info->treeview)); | 1325 | row = trace_view_get_selected_row(GTK_WIDGET(info->treeview)); |
@@ -559,29 +1337,59 @@ do_tree_popup(GtkWidget *widget, GdkEventButton *event, gpointer data) | |||
559 | 1337 | ||
560 | len = strlen(comm) + 50; | 1338 | len = strlen(comm) + 50; |
561 | 1339 | ||
562 | text = g_malloc(len); | 1340 | if (info->sync_task_filters) { |
563 | g_assert(text); | 1341 | if (trace_graph_filter_task_find_pid(ginfo, pid)) |
564 | 1342 | set_menu_label(menu_filter_add_task, comm, pid, | |
565 | if (trace_graph_filter_task_find_pid(ginfo, pid)) | 1343 | "Remove %s-%d from filters"); |
566 | snprintf(text, len, "Remove %s-%d to filter", comm, pid); | 1344 | else |
567 | else | 1345 | set_menu_label(menu_filter_add_task, comm, pid, |
568 | snprintf(text, len, "Add %s-%d to filter", comm, pid); | 1346 | "Add %s-%d to filters"); |
1347 | |||
1348 | if (trace_graph_hide_task_find_pid(ginfo, pid)) | ||
1349 | set_menu_label(menu_filter_hide_task, comm, pid, | ||
1350 | "Show %s-%d"); | ||
1351 | else | ||
1352 | set_menu_label(menu_filter_hide_task, comm, pid, | ||
1353 | "Hide %s-%d"); | ||
1354 | |||
1355 | gtk_widget_hide(menu_filter_graph_add_task); | ||
1356 | gtk_widget_hide(menu_filter_graph_hide_task); | ||
1357 | |||
1358 | } else { | ||
1359 | if (filter_task_find_pid(info->list_task_filter, pid)) | ||
1360 | set_menu_label(menu_filter_add_task, comm, pid, | ||
1361 | "Remove %s-%d from List filter"); | ||
1362 | else | ||
1363 | set_menu_label(menu_filter_add_task, comm, pid, | ||
1364 | "Add %s-%d to List filter"); | ||
1365 | |||
1366 | if (filter_task_find_pid(info->list_hide_tasks, pid)) | ||
1367 | set_menu_label(menu_filter_hide_task, comm, pid, | ||
1368 | "Show %s-%d in List"); | ||
1369 | else | ||
1370 | set_menu_label(menu_filter_hide_task, comm, pid, | ||
1371 | "Hide %s-%d from List"); | ||
1372 | |||
1373 | if (trace_graph_filter_task_find_pid(ginfo, pid)) | ||
1374 | set_menu_label(menu_filter_graph_add_task, comm, pid, | ||
1375 | "Remove %s-%d from Graph filter"); | ||
1376 | else | ||
1377 | set_menu_label(menu_filter_graph_add_task, comm, pid, | ||
1378 | "Add %s-%d to Graph filter"); | ||
1379 | |||
1380 | if (trace_graph_hide_task_find_pid(ginfo, pid)) | ||
1381 | set_menu_label(menu_filter_graph_hide_task, comm, pid, | ||
1382 | "Show %s-%d in Graph"); | ||
1383 | else | ||
1384 | set_menu_label(menu_filter_graph_hide_task, comm, pid, | ||
1385 | "Hide %s-%d from Graph"); | ||
1386 | |||
1387 | gtk_widget_show(menu_filter_graph_add_task); | ||
1388 | gtk_widget_show(menu_filter_graph_hide_task); | ||
1389 | } | ||
569 | 1390 | ||
570 | ginfo->filter_task_selected = pid; | 1391 | ginfo->filter_task_selected = pid; |
571 | 1392 | ||
572 | gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_add_task), | ||
573 | text); | ||
574 | |||
575 | if (trace_graph_hide_task_find_pid(ginfo, pid)) | ||
576 | snprintf(text, len, "Show %s-%d", comm, pid); | ||
577 | else | ||
578 | snprintf(text, len, "Hide %s-%d", comm, pid); | ||
579 | |||
580 | gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_hide_task), | ||
581 | text); | ||
582 | |||
583 | g_free(text); | ||
584 | |||
585 | info->selected_task = pid; | 1393 | info->selected_task = pid; |
586 | 1394 | ||
587 | gtk_widget_show(menu_filter_add_task); | 1395 | gtk_widget_show(menu_filter_add_task); |
@@ -591,35 +1399,62 @@ do_tree_popup(GtkWidget *widget, GdkEventButton *event, gpointer data) | |||
591 | } else { | 1399 | } else { |
592 | gtk_widget_hide(menu_filter_add_task); | 1400 | gtk_widget_hide(menu_filter_add_task); |
593 | gtk_widget_hide(menu_filter_hide_task); | 1401 | gtk_widget_hide(menu_filter_hide_task); |
1402 | gtk_widget_hide(menu_filter_graph_add_task); | ||
1403 | gtk_widget_hide(menu_filter_graph_hide_task); | ||
594 | } | 1404 | } |
595 | 1405 | ||
596 | if (ginfo->filter_enabled) | 1406 | if (ginfo->filter_enabled) |
597 | gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_graph_enable), | 1407 | gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_graph_enable), |
598 | "Disable Graph Filter"); | 1408 | "Disable Graph Task Filter"); |
599 | else | 1409 | else |
600 | gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_graph_enable), | 1410 | gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_graph_enable), |
601 | "Enable Graph Filter"); | 1411 | "Enable Graph Task Filter"); |
602 | 1412 | ||
603 | if (info->list_filter_enabled) | 1413 | if (info->list_filter_enabled) |
604 | gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_list_enable), | 1414 | gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_list_enable), |
605 | "Disable List Filter"); | 1415 | "Disable List Task Filter"); |
606 | else | 1416 | else |
607 | gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_list_enable), | 1417 | gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_list_enable), |
608 | "Enable List Filter"); | 1418 | "Enable List Task Filter"); |
609 | 1419 | ||
610 | if (ginfo->filter_available) { | 1420 | if (ginfo->filter_available) |
611 | gtk_widget_set_sensitive(menu_filter_graph_enable, TRUE); | 1421 | gtk_widget_set_sensitive(menu_filter_graph_enable, TRUE); |
612 | gtk_widget_set_sensitive(menu_filter_list_enable, TRUE); | 1422 | else |
613 | } else { | ||
614 | gtk_widget_set_sensitive(menu_filter_graph_enable, FALSE); | 1423 | gtk_widget_set_sensitive(menu_filter_graph_enable, FALSE); |
615 | gtk_widget_set_sensitive(menu_filter_list_enable, FALSE); | ||
616 | } | ||
617 | 1424 | ||
618 | if (filter_task_count(ginfo->task_filter) || | 1425 | if ((info->sync_task_filters && ginfo->filter_available) || |
619 | filter_task_count(ginfo->hide_tasks)) | 1426 | (!info->sync_task_filters && info->list_filter_available)) |
620 | gtk_widget_set_sensitive(menu_filter_clear_tasks, TRUE); | 1427 | gtk_widget_set_sensitive(menu_filter_list_enable, TRUE); |
621 | else | 1428 | else |
622 | gtk_widget_set_sensitive(menu_filter_clear_tasks, FALSE); | 1429 | gtk_widget_set_sensitive(menu_filter_list_enable, FALSE); |
1430 | |||
1431 | if (info->sync_task_filters) { | ||
1432 | if (filter_task_count(ginfo->task_filter) || | ||
1433 | filter_task_count(ginfo->hide_tasks)) | ||
1434 | gtk_widget_set_sensitive(menu_filter_clear_tasks, TRUE); | ||
1435 | else | ||
1436 | gtk_widget_set_sensitive(menu_filter_clear_tasks, FALSE); | ||
1437 | |||
1438 | set_menu_label(menu_filter_clear_tasks, comm, pid, | ||
1439 | "Clear Task Filter"); | ||
1440 | gtk_widget_hide(menu_filter_graph_clear_tasks); | ||
1441 | } else { | ||
1442 | if (filter_task_count(ginfo->task_filter) || | ||
1443 | filter_task_count(ginfo->hide_tasks)) | ||
1444 | gtk_widget_set_sensitive(menu_filter_graph_clear_tasks, TRUE); | ||
1445 | else | ||
1446 | gtk_widget_set_sensitive(menu_filter_graph_clear_tasks, FALSE); | ||
1447 | |||
1448 | if (filter_task_count(info->list_task_filter) || | ||
1449 | filter_task_count(info->list_hide_tasks)) | ||
1450 | gtk_widget_set_sensitive(menu_filter_clear_tasks, TRUE); | ||
1451 | else | ||
1452 | gtk_widget_set_sensitive(menu_filter_clear_tasks, FALSE); | ||
1453 | |||
1454 | set_menu_label(menu_filter_clear_tasks, comm, pid, | ||
1455 | "Clear List Task Filter"); | ||
1456 | gtk_widget_show(menu_filter_graph_clear_tasks); | ||
1457 | } | ||
623 | 1458 | ||
624 | gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, | 1459 | gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, |
625 | gtk_get_current_event_time()); | 1460 | gtk_get_current_event_time()); |
@@ -655,6 +1490,7 @@ void kernel_shark(int argc, char **argv) | |||
655 | GtkWidget *label; | 1490 | GtkWidget *label; |
656 | GtkWidget *spin; | 1491 | GtkWidget *spin; |
657 | GtkWidget *check; | 1492 | GtkWidget *check; |
1493 | GtkWidget *statusbar; | ||
658 | int ret; | 1494 | int ret; |
659 | int c; | 1495 | int c; |
660 | 1496 | ||
@@ -701,12 +1537,16 @@ void kernel_shark(int argc, char **argv) | |||
701 | handle = NULL; | 1537 | handle = NULL; |
702 | 1538 | ||
703 | info->handle = handle; | 1539 | info->handle = handle; |
1540 | info->sync_task_filters = TRUE; | ||
1541 | info->sync_event_filters = TRUE; | ||
704 | 1542 | ||
705 | /* --- Main window --- */ | 1543 | /* --- Main window --- */ |
706 | 1544 | ||
707 | window = gtk_window_new(GTK_WINDOW_TOPLEVEL); | 1545 | window = gtk_window_new(GTK_WINDOW_TOPLEVEL); |
708 | info->window = window; | 1546 | info->window = window; |
709 | 1547 | ||
1548 | trace_dialog_register_window(window); | ||
1549 | |||
710 | if (input_file) | 1550 | if (input_file) |
711 | update_title(window, input_file); | 1551 | update_title(window, input_file); |
712 | 1552 | ||
@@ -734,12 +1574,11 @@ void kernel_shark(int argc, char **argv) | |||
734 | 1574 | ||
735 | /* --- File - Load Option --- */ | 1575 | /* --- File - Load Option --- */ |
736 | 1576 | ||
737 | sub_item = gtk_menu_item_new_with_label("Load info"); | 1577 | sub_item = gtk_menu_item_new_with_label("Load data"); |
738 | 1578 | ||
739 | /* Add them to the menu */ | 1579 | /* Add them to the menu */ |
740 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); | 1580 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); |
741 | 1581 | ||
742 | /* We can attach the Quit menu item to our exit function */ | ||
743 | g_signal_connect_swapped (G_OBJECT (sub_item), "activate", | 1582 | g_signal_connect_swapped (G_OBJECT (sub_item), "activate", |
744 | G_CALLBACK (load_clicked), | 1583 | G_CALLBACK (load_clicked), |
745 | (gpointer) info); | 1584 | (gpointer) info); |
@@ -748,6 +1587,35 @@ void kernel_shark(int argc, char **argv) | |||
748 | gtk_widget_show(sub_item); | 1587 | gtk_widget_show(sub_item); |
749 | 1588 | ||
750 | 1589 | ||
1590 | /* --- File - Load Filter Option --- */ | ||
1591 | |||
1592 | sub_item = gtk_menu_item_new_with_label("Load filters"); | ||
1593 | |||
1594 | /* Add them to the menu */ | ||
1595 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); | ||
1596 | |||
1597 | g_signal_connect_swapped (G_OBJECT (sub_item), "activate", | ||
1598 | G_CALLBACK (load_filters_clicked), | ||
1599 | (gpointer) info); | ||
1600 | |||
1601 | /* We do need to show menu items */ | ||
1602 | gtk_widget_show(sub_item); | ||
1603 | |||
1604 | |||
1605 | /* --- File - Save Filter Option --- */ | ||
1606 | |||
1607 | sub_item = gtk_menu_item_new_with_label("Save filters"); | ||
1608 | |||
1609 | /* Add them to the menu */ | ||
1610 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); | ||
1611 | |||
1612 | g_signal_connect_swapped (G_OBJECT (sub_item), "activate", | ||
1613 | G_CALLBACK (save_filters_clicked), | ||
1614 | (gpointer) info); | ||
1615 | |||
1616 | /* We do need to show menu items */ | ||
1617 | gtk_widget_show(sub_item); | ||
1618 | |||
751 | /* --- File - Quit Option --- */ | 1619 | /* --- File - Quit Option --- */ |
752 | 1620 | ||
753 | sub_item = gtk_menu_item_new_with_label("Quit"); | 1621 | sub_item = gtk_menu_item_new_with_label("Quit"); |
@@ -777,66 +1645,145 @@ void kernel_shark(int argc, char **argv) | |||
777 | menu = gtk_menu_new(); /* Don't need to show menus */ | 1645 | menu = gtk_menu_new(); /* Don't need to show menus */ |
778 | 1646 | ||
779 | 1647 | ||
780 | /* --- Filter - List Events Option --- */ | ||
781 | 1648 | ||
782 | sub_item = gtk_menu_item_new_with_label("list events"); | 1649 | /* --- Filter - Sync task Option --- */ |
1650 | |||
1651 | sub_item = gtk_menu_item_new_with_label("Unsync Graph and List Task Filters"); | ||
1652 | |||
1653 | info->task_sync_menu = sub_item; | ||
783 | 1654 | ||
784 | /* Add them to the menu */ | 1655 | /* Add them to the menu */ |
785 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); | 1656 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); |
786 | 1657 | ||
787 | /* We can attach the Quit menu item to our exit function */ | 1658 | /* We can attach the Quit menu item to our exit function */ |
788 | g_signal_connect_swapped (G_OBJECT (sub_item), "activate", | 1659 | g_signal_connect (G_OBJECT (sub_item), "activate", |
789 | G_CALLBACK (list_events_clicked), | 1660 | G_CALLBACK (sync_task_filter_clicked), |
790 | (gpointer) info); | 1661 | (gpointer) info); |
791 | 1662 | ||
792 | /* We do need to show menu items */ | 1663 | /* We do need to show menu items */ |
793 | gtk_widget_show(sub_item); | 1664 | gtk_widget_show(sub_item); |
794 | 1665 | ||
795 | 1666 | ||
796 | /* --- Filter - Events Option --- */ | 1667 | /* --- Filter - Sync events Option --- */ |
1668 | |||
1669 | sub_item = gtk_menu_item_new_with_label("Unsync Graph and List Event Filters"); | ||
797 | 1670 | ||
798 | sub_item = gtk_menu_item_new_with_label("graph events"); | 1671 | info->events_sync_menu = sub_item; |
1672 | |||
1673 | /* Add them to the menu */ | ||
1674 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); | ||
1675 | |||
1676 | /* We can attach the Quit menu item to our exit function */ | ||
1677 | g_signal_connect (G_OBJECT (sub_item), "activate", | ||
1678 | G_CALLBACK (sync_events_filter_clicked), | ||
1679 | (gpointer) info); | ||
1680 | |||
1681 | /* We do need to show menu items */ | ||
1682 | gtk_widget_show(sub_item); | ||
1683 | |||
1684 | |||
1685 | /* --- Filter - List Tasks Option --- */ | ||
1686 | |||
1687 | sub_item = gtk_menu_item_new_with_label("list tasks"); | ||
799 | 1688 | ||
800 | /* Add them to the menu */ | 1689 | /* Add them to the menu */ |
801 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); | 1690 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); |
802 | 1691 | ||
803 | /* We can attach the Quit menu item to our exit function */ | 1692 | /* We can attach the Quit menu item to our exit function */ |
804 | g_signal_connect_swapped (G_OBJECT (sub_item), "activate", | 1693 | g_signal_connect_swapped (G_OBJECT (sub_item), "activate", |
805 | G_CALLBACK (graph_events_clicked), | 1694 | G_CALLBACK (list_tasks_clicked), |
1695 | (gpointer) info); | ||
1696 | |||
1697 | info->list_task_menu = sub_item; | ||
1698 | |||
1699 | /* Only show this item when list and graph tasks are not synced */ | ||
1700 | |||
1701 | |||
1702 | /* --- Filter - Graph Tasks Option --- */ | ||
1703 | |||
1704 | sub_item = gtk_menu_item_new_with_label("tasks"); | ||
1705 | |||
1706 | /* Add them to the menu */ | ||
1707 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); | ||
1708 | |||
1709 | /* We can attach the Quit menu item to our exit function */ | ||
1710 | g_signal_connect_swapped (G_OBJECT (sub_item), "activate", | ||
1711 | G_CALLBACK (graph_tasks_clicked), | ||
806 | (gpointer) info); | 1712 | (gpointer) info); |
807 | 1713 | ||
1714 | info->graph_task_menu = sub_item; | ||
1715 | |||
808 | /* We do need to show menu items */ | 1716 | /* We do need to show menu items */ |
809 | gtk_widget_show(sub_item); | 1717 | gtk_widget_show(sub_item); |
810 | 1718 | ||
811 | 1719 | ||
812 | /* --- Filter - Events Option --- */ | 1720 | /* --- Filter - List Hide Tasks Option --- */ |
813 | 1721 | ||
814 | sub_item = gtk_menu_item_new_with_label("sync graph events with list"); | 1722 | sub_item = gtk_menu_item_new_with_label("list hide tasks"); |
815 | 1723 | ||
816 | /* Add them to the menu */ | 1724 | /* Add them to the menu */ |
817 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); | 1725 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); |
818 | 1726 | ||
819 | /* We can attach the Quit menu item to our exit function */ | 1727 | /* We can attach the Quit menu item to our exit function */ |
820 | g_signal_connect_swapped (G_OBJECT (sub_item), "activate", | 1728 | g_signal_connect_swapped (G_OBJECT (sub_item), "activate", |
821 | G_CALLBACK (sync_graph_events_to_list_clicked), | 1729 | G_CALLBACK (list_hide_tasks_clicked), |
822 | (gpointer) info); | 1730 | (gpointer) info); |
823 | 1731 | ||
1732 | info->list_hide_task_menu = sub_item; | ||
1733 | |||
1734 | /* Only show this item when list and graph tasks are not synced */ | ||
1735 | |||
1736 | |||
1737 | /* --- Filter - Graph Hide Tasks Option --- */ | ||
1738 | |||
1739 | sub_item = gtk_menu_item_new_with_label("hide tasks"); | ||
1740 | |||
1741 | /* Add them to the menu */ | ||
1742 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); | ||
1743 | |||
1744 | /* We can attach the Quit menu item to our exit function */ | ||
1745 | g_signal_connect_swapped (G_OBJECT (sub_item), "activate", | ||
1746 | G_CALLBACK (graph_hide_tasks_clicked), | ||
1747 | (gpointer) info); | ||
1748 | |||
1749 | info->graph_hide_task_menu = sub_item; | ||
1750 | |||
824 | /* We do need to show menu items */ | 1751 | /* We do need to show menu items */ |
825 | gtk_widget_show(sub_item); | 1752 | gtk_widget_show(sub_item); |
826 | 1753 | ||
827 | 1754 | ||
1755 | /* --- Filter - List Events Option --- */ | ||
1756 | |||
1757 | sub_item = gtk_menu_item_new_with_label("list events"); | ||
1758 | |||
1759 | /* Add them to the menu */ | ||
1760 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); | ||
1761 | |||
1762 | /* We can attach the Quit menu item to our exit function */ | ||
1763 | g_signal_connect_swapped (G_OBJECT (sub_item), "activate", | ||
1764 | G_CALLBACK (list_events_clicked), | ||
1765 | (gpointer) info); | ||
1766 | |||
1767 | info->list_events_menu = sub_item; | ||
1768 | |||
1769 | /* We do not show this menu (yet) */ | ||
1770 | |||
1771 | |||
828 | /* --- Filter - Events Option --- */ | 1772 | /* --- Filter - Events Option --- */ |
829 | 1773 | ||
830 | sub_item = gtk_menu_item_new_with_label("sync list events with graph"); | 1774 | /* The list and graph events start off insync */ |
1775 | sub_item = gtk_menu_item_new_with_label("events"); | ||
831 | 1776 | ||
832 | /* Add them to the menu */ | 1777 | /* Add them to the menu */ |
833 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); | 1778 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); |
834 | 1779 | ||
835 | /* We can attach the Quit menu item to our exit function */ | 1780 | /* We can attach the Quit menu item to our exit function */ |
836 | g_signal_connect_swapped (G_OBJECT (sub_item), "activate", | 1781 | g_signal_connect_swapped (G_OBJECT (sub_item), "activate", |
837 | G_CALLBACK (sync_list_events_to_graph_clicked), | 1782 | G_CALLBACK (graph_events_clicked), |
838 | (gpointer) info); | 1783 | (gpointer) info); |
839 | 1784 | ||
1785 | info->graph_events_menu = sub_item; | ||
1786 | |||
840 | /* We do need to show menu items */ | 1787 | /* We do need to show menu items */ |
841 | gtk_widget_show(sub_item); | 1788 | gtk_widget_show(sub_item); |
842 | 1789 | ||
@@ -853,13 +1800,14 @@ void kernel_shark(int argc, char **argv) | |||
853 | G_CALLBACK (adv_list_filter_clicked), | 1800 | G_CALLBACK (adv_list_filter_clicked), |
854 | (gpointer) info); | 1801 | (gpointer) info); |
855 | 1802 | ||
856 | /* We do need to show menu items */ | 1803 | info->list_adv_events_menu = sub_item; |
857 | gtk_widget_show(sub_item); | 1804 | /* We do not show this menu (yet) */ |
858 | 1805 | ||
859 | 1806 | ||
860 | /* --- Filter - Graph Advanced Events Option --- */ | 1807 | /* --- Filter - Graph Advanced Events Option --- */ |
861 | 1808 | ||
862 | sub_item = gtk_menu_item_new_with_label("graph advanced event"); | 1809 | /* The list and graph events start off in sync */ |
1810 | sub_item = gtk_menu_item_new_with_label("advanced events"); | ||
863 | 1811 | ||
864 | /* Add them to the menu */ | 1812 | /* Add them to the menu */ |
865 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); | 1813 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); |
@@ -869,6 +1817,8 @@ void kernel_shark(int argc, char **argv) | |||
869 | G_CALLBACK (adv_graph_filter_clicked), | 1817 | G_CALLBACK (adv_graph_filter_clicked), |
870 | (gpointer) info); | 1818 | (gpointer) info); |
871 | 1819 | ||
1820 | info->graph_adv_events_menu = sub_item; | ||
1821 | |||
872 | /* We do need to show menu items */ | 1822 | /* We do need to show menu items */ |
873 | gtk_widget_show(sub_item); | 1823 | gtk_widget_show(sub_item); |
874 | 1824 | ||
@@ -939,6 +1889,53 @@ void kernel_shark(int argc, char **argv) | |||
939 | gtk_menu_item_set_submenu(GTK_MENU_ITEM (menu_item), menu); | 1889 | gtk_menu_item_set_submenu(GTK_MENU_ITEM (menu_item), menu); |
940 | 1890 | ||
941 | 1891 | ||
1892 | |||
1893 | /* --- Help Option --- */ | ||
1894 | |||
1895 | menu_item = gtk_menu_item_new_with_label("Help"); | ||
1896 | gtk_widget_show(menu_item); | ||
1897 | |||
1898 | gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), menu_item); | ||
1899 | |||
1900 | menu = gtk_menu_new(); /* Don't need to show menus */ | ||
1901 | |||
1902 | |||
1903 | /* --- Help - Contents Option --- */ | ||
1904 | |||
1905 | sub_item = gtk_menu_item_new_with_label("Contents"); | ||
1906 | |||
1907 | /* Add them to the menu */ | ||
1908 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); | ||
1909 | |||
1910 | /* We can attach the Quit menu item to our exit function */ | ||
1911 | g_signal_connect_swapped (G_OBJECT (sub_item), "activate", | ||
1912 | G_CALLBACK (help_content_clicked), | ||
1913 | (gpointer) info); | ||
1914 | |||
1915 | /* We do need to show menu items */ | ||
1916 | gtk_widget_show(sub_item); | ||
1917 | |||
1918 | |||
1919 | /* --- Help - About Option --- */ | ||
1920 | |||
1921 | sub_item = gtk_menu_item_new_with_label("About"); | ||
1922 | |||
1923 | /* Add them to the menu */ | ||
1924 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); | ||
1925 | |||
1926 | /* We can attach the Quit menu item to our exit function */ | ||
1927 | g_signal_connect_swapped (G_OBJECT (sub_item), "activate", | ||
1928 | G_CALLBACK (help_about_clicked), | ||
1929 | (gpointer) info); | ||
1930 | |||
1931 | /* We do need to show menu items */ | ||
1932 | gtk_widget_show(sub_item); | ||
1933 | |||
1934 | |||
1935 | /* --- End Help Options --- */ | ||
1936 | gtk_menu_item_set_submenu(GTK_MENU_ITEM (menu_item), menu); | ||
1937 | |||
1938 | |||
942 | /* --- Top Level Vpaned --- */ | 1939 | /* --- Top Level Vpaned --- */ |
943 | 1940 | ||
944 | vpaned = gtk_vpaned_new(); | 1941 | vpaned = gtk_vpaned_new(); |
@@ -1033,6 +2030,12 @@ void kernel_shark(int argc, char **argv) | |||
1033 | 2030 | ||
1034 | gtk_widget_show(info->treeview); | 2031 | gtk_widget_show(info->treeview); |
1035 | 2032 | ||
2033 | /* --- Set up Status Bar --- */ | ||
2034 | |||
2035 | statusbar = trace_status_bar_new(); | ||
2036 | |||
2037 | gtk_box_pack_start(GTK_BOX(vbox), statusbar, FALSE, FALSE, 0); | ||
2038 | gtk_widget_show(statusbar); | ||
1036 | 2039 | ||
1037 | /********************************************** | 2040 | /********************************************** |
1038 | * Main Window | 2041 | * Main Window |
diff --git a/kernel-shark.h b/kernel-shark.h index edb3787..a808108 100644 --- a/kernel-shark.h +++ b/kernel-shark.h | |||
@@ -30,10 +30,25 @@ struct shark_info { | |||
30 | struct tracecmd_input *handle; | 30 | struct tracecmd_input *handle; |
31 | GtkWidget *treeview; | 31 | GtkWidget *treeview; |
32 | GtkWidget *spin; | 32 | GtkWidget *spin; |
33 | GtkWidget *task_sync_menu; | ||
34 | GtkWidget *events_sync_menu; | ||
35 | GtkWidget *list_task_menu; | ||
36 | GtkWidget *graph_task_menu; | ||
37 | GtkWidget *list_hide_task_menu; | ||
38 | GtkWidget *graph_hide_task_menu; | ||
39 | GtkWidget *list_events_menu; | ||
40 | GtkWidget *graph_events_menu; | ||
41 | GtkWidget *list_adv_events_menu; | ||
42 | GtkWidget *graph_adv_events_menu; | ||
33 | struct graph_callbacks graph_cbs; | 43 | struct graph_callbacks graph_cbs; |
34 | gint selected_task; | 44 | gint selected_task; |
35 | gboolean list_filter_enabled; | 45 | gboolean list_filter_enabled; |
46 | gboolean list_filter_available; | ||
36 | gboolean graph_follows; | 47 | gboolean graph_follows; |
48 | gboolean sync_task_filters; | ||
49 | gboolean sync_event_filters; | ||
50 | struct filter_task *list_task_filter; | ||
51 | struct filter_task *list_hide_tasks; | ||
37 | }; | 52 | }; |
38 | 53 | ||
39 | #define offset_of(type, field) (long)(&((type *)0)->field) | 54 | #define offset_of(type, field) (long)(&((type *)0)->field) |
diff --git a/parse-events.h b/parse-events.h index bb2fe83..32a8ff0 100644 --- a/parse-events.h +++ b/parse-events.h | |||
@@ -347,11 +347,21 @@ struct pevent { | |||
347 | struct event_format *last_event; | 347 | struct event_format *last_event; |
348 | }; | 348 | }; |
349 | 349 | ||
350 | /* Can be overridden */ | ||
350 | void die(char *fmt, ...); | 351 | void die(char *fmt, ...); |
351 | void *malloc_or_die(unsigned int size); | 352 | void *malloc_or_die(unsigned int size); |
352 | void warning(char *fmt, ...); | 353 | void warning(char *fmt, ...); |
353 | void pr_stat(char *fmt, ...); | 354 | void pr_stat(char *fmt, ...); |
354 | 355 | ||
356 | /* Always available */ | ||
357 | void __die(char *fmt, ...); | ||
358 | void __warning(char *fmt, ...); | ||
359 | void __pr_stat(char *fmt, ...); | ||
360 | |||
361 | void __vdie(char *fmt, ...); | ||
362 | void __vwarning(char *fmt, ...); | ||
363 | void __vpr_stat(char *fmt, ...); | ||
364 | |||
355 | static inline unsigned short | 365 | static inline unsigned short |
356 | __data2host2(struct pevent *pevent, unsigned short data) | 366 | __data2host2(struct pevent *pevent, unsigned short data) |
357 | { | 367 | { |
@@ -702,4 +712,6 @@ int pevent_filter_copy(struct event_filter *dest, struct event_filter *source); | |||
702 | int pevent_update_trivial(struct event_filter *dest, struct event_filter *source, | 712 | int pevent_update_trivial(struct event_filter *dest, struct event_filter *source, |
703 | enum filter_trivial_type type); | 713 | enum filter_trivial_type type); |
704 | 714 | ||
715 | int pevent_filter_compare(struct event_filter *filter1, struct event_filter *filter2); | ||
716 | |||
705 | #endif /* _PARSE_EVENTS_H */ | 717 | #endif /* _PARSE_EVENTS_H */ |
diff --git a/parse-filter.c b/parse-filter.c index 4efce04..75512e3 100644 --- a/parse-filter.c +++ b/parse-filter.c | |||
@@ -2026,3 +2026,60 @@ pevent_filter_make_string(struct event_filter *filter, int event_id) | |||
2026 | return arg_to_str(filter, filter_type->filter); | 2026 | return arg_to_str(filter, filter_type->filter); |
2027 | } | 2027 | } |
2028 | 2028 | ||
2029 | /** | ||
2030 | * pevent_filter_compare - compare two filters and return if they are the same | ||
2031 | * @filter1: Filter to compare with @filter2 | ||
2032 | * @filter2: Filter to compare with @filter1 | ||
2033 | * | ||
2034 | * Returns: | ||
2035 | * 1 if the two filters hold the same content. | ||
2036 | * 0 if they do not. | ||
2037 | */ | ||
2038 | int pevent_filter_compare(struct event_filter *filter1, struct event_filter *filter2) | ||
2039 | { | ||
2040 | struct filter_type *filter_type1; | ||
2041 | struct filter_type *filter_type2; | ||
2042 | char *str1, *str2; | ||
2043 | int result; | ||
2044 | int i; | ||
2045 | |||
2046 | /* Do the easy checks first */ | ||
2047 | if (filter1->filters != filter2->filters) | ||
2048 | return 0; | ||
2049 | if (!filter1->filters && !filter2->filters) | ||
2050 | return 1; | ||
2051 | |||
2052 | /* | ||
2053 | * Now take a look at each of the events to see if they have the same | ||
2054 | * filters to them. | ||
2055 | */ | ||
2056 | for (i = 0; i < filter1->filters; i++) { | ||
2057 | filter_type1 = &filter1->event_filters[i]; | ||
2058 | filter_type2 = find_filter_type(filter2, filter_type1->event_id); | ||
2059 | if (!filter_type2) | ||
2060 | break; | ||
2061 | if (filter_type1->filter->type != filter_type2->filter->type) | ||
2062 | break; | ||
2063 | switch (filter_type1->filter->type) { | ||
2064 | case FILTER_TRIVIAL_FALSE: | ||
2065 | case FILTER_TRIVIAL_TRUE: | ||
2066 | /* trivial types just need the type compared */ | ||
2067 | continue; | ||
2068 | default: | ||
2069 | break; | ||
2070 | } | ||
2071 | /* The best way to compare complex filters is with strings */ | ||
2072 | str1 = arg_to_str(filter1, filter_type1->filter); | ||
2073 | str2 = arg_to_str(filter2, filter_type2->filter); | ||
2074 | result = strcmp(str1, str2) != 0; | ||
2075 | free(str1); | ||
2076 | free(str2); | ||
2077 | if (result) | ||
2078 | break; | ||
2079 | } | ||
2080 | |||
2081 | if (i < filter1->filters) | ||
2082 | return 0; | ||
2083 | return 1; | ||
2084 | } | ||
2085 | |||
diff --git a/parse-utils.c b/parse-utils.c new file mode 100644 index 0000000..103bbd8 --- /dev/null +++ b/parse-utils.c | |||
@@ -0,0 +1,105 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <string.h> | ||
4 | #include <stdarg.h> | ||
5 | #include <errno.h> | ||
6 | |||
7 | #define __weak __attribute__((weak)) | ||
8 | |||
9 | void __vdie(char *fmt, va_list ap) | ||
10 | { | ||
11 | int ret = errno; | ||
12 | |||
13 | if (errno) | ||
14 | perror("trace-cmd"); | ||
15 | else | ||
16 | ret = -1; | ||
17 | |||
18 | fprintf(stderr, " "); | ||
19 | vfprintf(stderr, fmt, ap); | ||
20 | |||
21 | fprintf(stderr, "\n"); | ||
22 | exit(ret); | ||
23 | } | ||
24 | |||
25 | void __die(char *fmt, ...) | ||
26 | { | ||
27 | va_list ap; | ||
28 | |||
29 | va_start(ap, fmt); | ||
30 | __vdie(fmt, ap); | ||
31 | va_end(ap); | ||
32 | } | ||
33 | |||
34 | void __weak die(char *fmt, ...) | ||
35 | { | ||
36 | va_list ap; | ||
37 | |||
38 | va_start(ap, fmt); | ||
39 | __vdie(fmt, ap); | ||
40 | va_end(ap); | ||
41 | } | ||
42 | |||
43 | void __vwarning(char *fmt, va_list ap) | ||
44 | { | ||
45 | if (errno) | ||
46 | perror("trace-cmd"); | ||
47 | errno = 0; | ||
48 | |||
49 | fprintf(stderr, " "); | ||
50 | vfprintf(stderr, fmt, ap); | ||
51 | |||
52 | fprintf(stderr, "\n"); | ||
53 | } | ||
54 | |||
55 | void __warning(char *fmt, ...) | ||
56 | { | ||
57 | va_list ap; | ||
58 | |||
59 | va_start(ap, fmt); | ||
60 | __vwarning(fmt, ap); | ||
61 | va_end(ap); | ||
62 | } | ||
63 | |||
64 | void __weak warning(char *fmt, ...) | ||
65 | { | ||
66 | va_list ap; | ||
67 | |||
68 | va_start(ap, fmt); | ||
69 | __vwarning(fmt, ap); | ||
70 | va_end(ap); | ||
71 | } | ||
72 | |||
73 | void __vpr_stat(char *fmt, va_list ap) | ||
74 | { | ||
75 | vprintf(fmt, ap); | ||
76 | printf("\n"); | ||
77 | } | ||
78 | |||
79 | void __pr_stat(char *fmt, ...) | ||
80 | { | ||
81 | va_list ap; | ||
82 | |||
83 | va_start(ap, fmt); | ||
84 | __vpr_stat(fmt, ap); | ||
85 | va_end(ap); | ||
86 | } | ||
87 | |||
88 | void __weak pr_stat(char *fmt, ...) | ||
89 | { | ||
90 | va_list ap; | ||
91 | |||
92 | va_start(ap, fmt); | ||
93 | __vpr_stat(fmt, ap); | ||
94 | va_end(ap); | ||
95 | } | ||
96 | |||
97 | void __weak *malloc_or_die(unsigned int size) | ||
98 | { | ||
99 | void *data; | ||
100 | |||
101 | data = malloc(size); | ||
102 | if (!data) | ||
103 | die("malloc"); | ||
104 | return data; | ||
105 | } | ||
diff --git a/trace-compat.c b/trace-compat.c index 5082757..e1652e0 100644 --- a/trace-compat.c +++ b/trace-compat.c | |||
@@ -22,6 +22,10 @@ | |||
22 | * Linux Kernel that were written by Frederic Weisbecker. | 22 | * Linux Kernel that were written by Frederic Weisbecker. |
23 | */ | 23 | */ |
24 | #include "trace-compat.h" | 24 | #include "trace-compat.h" |
25 | #include "trace-gui.h" | ||
26 | #include "trace-cmd.h" | ||
27 | |||
28 | #include <gdk/gdk.h> | ||
25 | 29 | ||
26 | #if GTK_VERSION < CALC_GTK_VERSION(2,18,0) | 30 | #if GTK_VERSION < CALC_GTK_VERSION(2,18,0) |
27 | 31 | ||
@@ -70,6 +74,24 @@ gdouble gtk_adjustment_get_lower(GtkAdjustment *adj) | |||
70 | return adj->lower; | 74 | return adj->lower; |
71 | } | 75 | } |
72 | 76 | ||
77 | gboolean gtk_show_uri(GdkScreen *screen, const gchar *uri, | ||
78 | guint32 timestamp, GError **error) | ||
79 | { | ||
80 | return FALSE; | ||
81 | } | ||
82 | |||
83 | void g_string_vprintf(GString *string, const gchar *format, va_list args) | ||
84 | { | ||
85 | char buf[1024]; | ||
86 | gint len; | ||
87 | |||
88 | len = vsnprintf(buf, 1024, format, args); | ||
89 | if (len >= 1024) | ||
90 | die("compat g_string_vprintf can not process length of %d\n", len); | ||
91 | |||
92 | g_string_printf(string, "%s", buf); | ||
93 | } | ||
94 | |||
73 | #endif /* version < 2.14.0 */ | 95 | #endif /* version < 2.14.0 */ |
74 | 96 | ||
75 | #if GTK_VERSION < CALC_GTK_VERSION(2,12,0) | 97 | #if GTK_VERSION < CALC_GTK_VERSION(2,12,0) |
@@ -79,4 +101,20 @@ GtkWidget *gtk_tree_view_column_get_tree_view(GtkTreeViewColumn *col) | |||
79 | return col->tree_view; | 101 | return col->tree_view; |
80 | } | 102 | } |
81 | 103 | ||
104 | void gtk_widget_set_tooltip_text(GtkWidget *widget, const gchar *text) | ||
105 | { | ||
106 | static GtkTooltips *tooltips; | ||
107 | |||
108 | /* Only works for widgets with windows, sorry */ | ||
109 | if (GTK_WIDGET_NO_WINDOW(widget)) | ||
110 | return; | ||
111 | |||
112 | if (!tooltips) { | ||
113 | tooltips = gtk_tooltips_new(); | ||
114 | gtk_tooltips_enable(tooltips); | ||
115 | } | ||
116 | |||
117 | gtk_tooltips_set_tip(GTK_TOOLTIPS(tooltips), widget, text, text); | ||
118 | } | ||
119 | |||
82 | #endif /* version < 2.12.0 */ | 120 | #endif /* version < 2.12.0 */ |
diff --git a/trace-compat.h b/trace-compat.h index 1c9126d..ce7759d 100644 --- a/trace-compat.h +++ b/trace-compat.h | |||
@@ -22,6 +22,7 @@ | |||
22 | #define _TRACE_COMPAT_H | 22 | #define _TRACE_COMPAT_H |
23 | 23 | ||
24 | #include <gtk/gtk.h> | 24 | #include <gtk/gtk.h> |
25 | #include <stdarg.h> | ||
25 | 26 | ||
26 | #define CALC_GTK_VERSION(maj, min, ext) ((maj << 16) + (min << 8) + ext) | 27 | #define CALC_GTK_VERSION(maj, min, ext) ((maj << 16) + (min << 8) + ext) |
27 | 28 | ||
@@ -43,12 +44,17 @@ void gtk_menu_item_set_label(GtkMenuItem *menu_item, const gchar *label); | |||
43 | gdouble gtk_adjustment_get_page_size(GtkAdjustment *adj); | 44 | gdouble gtk_adjustment_get_page_size(GtkAdjustment *adj); |
44 | gdouble gtk_adjustment_get_upper(GtkAdjustment *adj); | 45 | gdouble gtk_adjustment_get_upper(GtkAdjustment *adj); |
45 | gdouble gtk_adjustment_get_lower(GtkAdjustment *adj); | 46 | gdouble gtk_adjustment_get_lower(GtkAdjustment *adj); |
47 | gboolean gtk_show_uri(GdkScreen *screen, const gchar *uri, | ||
48 | guint32 timestamp, GError **error); | ||
49 | |||
50 | void g_string_vprintf(GString *string, const gchar *format, va_list args); | ||
46 | 51 | ||
47 | #endif /* version < 2.14.0 */ | 52 | #endif /* version < 2.14.0 */ |
48 | 53 | ||
49 | #if GTK_VERSION < CALC_GTK_VERSION(2,12,0) | 54 | #if GTK_VERSION < CALC_GTK_VERSION(2,12,0) |
50 | 55 | ||
51 | GtkWidget *gtk_tree_view_column_get_tree_view(GtkTreeViewColumn *col); | 56 | GtkWidget *gtk_tree_view_column_get_tree_view(GtkTreeViewColumn *col); |
57 | void gtk_widget_set_tooltip_text(GtkWidget *widget, const gchar *text); | ||
52 | 58 | ||
53 | #endif /* version < 2.12.0 */ | 59 | #endif /* version < 2.12.0 */ |
54 | 60 | ||
diff --git a/trace-dialog.c b/trace-dialog.c new file mode 100644 index 0000000..c220a52 --- /dev/null +++ b/trace-dialog.c | |||
@@ -0,0 +1,277 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> | ||
3 | * | ||
4 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; version 2 of the License (not later!) | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | * | ||
19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
20 | */ | ||
21 | #include <gtk/gtk.h> | ||
22 | #include <stdlib.h> | ||
23 | #include <string.h> | ||
24 | #include <stdarg.h> | ||
25 | #include <errno.h> | ||
26 | #include <ctype.h> | ||
27 | |||
28 | #include "trace-compat.h" | ||
29 | #include "trace-cmd.h" | ||
30 | #include "trace-gui.h" | ||
31 | |||
32 | #define DIALOG_WIDTH 500 | ||
33 | #define DIALOG_HEIGHT 550 | ||
34 | |||
35 | static GtkWidget *statusbar; | ||
36 | static GtkWidget *statuspix; | ||
37 | static GString *statusstr; | ||
38 | |||
39 | static GtkWidget *parent_window; | ||
40 | |||
41 | void pr_stat(char *fmt, ...) | ||
42 | { | ||
43 | GString *str; | ||
44 | va_list ap; | ||
45 | |||
46 | if (!statusstr) { | ||
47 | statusstr = g_string_new(""); | ||
48 | if (!statusstr) | ||
49 | die("Allocating status string"); | ||
50 | } | ||
51 | |||
52 | str = g_string_new(""); | ||
53 | |||
54 | va_start(ap, fmt); | ||
55 | g_string_vprintf(str, fmt, ap); | ||
56 | va_end(ap); | ||
57 | |||
58 | g_string_append_printf(statusstr, "%s\n", str->str); | ||
59 | |||
60 | if (statusbar) { | ||
61 | gtk_statusbar_push(GTK_STATUSBAR(statusbar), 1, str->str); | ||
62 | gtk_widget_show(statuspix); | ||
63 | } | ||
64 | |||
65 | g_string_free(str, TRUE); | ||
66 | } | ||
67 | |||
68 | /** | ||
69 | * trace_dialog_register_window - register window for warning dialogs | ||
70 | * @window: parent window to use for other dialogs | ||
71 | * | ||
72 | * The warning messages do not have a way to pass the window to | ||
73 | * the function, since these functions are also used by the command | ||
74 | * line interface. This allows an application to give the warning | ||
75 | * messages a window to use. | ||
76 | */ | ||
77 | void trace_dialog_register_window(GtkWidget *window) | ||
78 | { | ||
79 | parent_window = window; | ||
80 | } | ||
81 | |||
82 | void warning(char *fmt, ...) | ||
83 | { | ||
84 | GString *str; | ||
85 | va_list ap; | ||
86 | |||
87 | if (!parent_window) { | ||
88 | va_start(ap, fmt); | ||
89 | __vwarning(fmt, ap); | ||
90 | va_end(ap); | ||
91 | return; | ||
92 | } | ||
93 | |||
94 | str = g_string_new(""); | ||
95 | |||
96 | va_start(ap, fmt); | ||
97 | g_string_vprintf(str, fmt, ap); | ||
98 | va_end(ap); | ||
99 | |||
100 | g_string_append(str, "\n"); | ||
101 | |||
102 | if (errno) | ||
103 | g_string_prepend(str, strerror(errno)); | ||
104 | |||
105 | errno = 0; | ||
106 | |||
107 | trace_dialog(GTK_WINDOW(parent_window), TRACE_GUI_WARNING, | ||
108 | str->str); | ||
109 | |||
110 | g_string_free(str, TRUE); | ||
111 | } | ||
112 | |||
113 | static void | ||
114 | status_display_clicked (gpointer data) | ||
115 | { | ||
116 | GtkWidget *dialog; | ||
117 | GtkWidget *scrollwin; | ||
118 | GtkWidget *viewport; | ||
119 | GtkWidget *textview; | ||
120 | GtkTextBuffer *buffer; | ||
121 | |||
122 | dialog = gtk_dialog_new_with_buttons("Status", | ||
123 | NULL, | ||
124 | GTK_DIALOG_MODAL, | ||
125 | "OK", | ||
126 | GTK_RESPONSE_ACCEPT, | ||
127 | NULL); | ||
128 | |||
129 | scrollwin = gtk_scrolled_window_new(NULL, NULL); | ||
130 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), | ||
131 | GTK_POLICY_AUTOMATIC, | ||
132 | GTK_POLICY_AUTOMATIC); | ||
133 | gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), scrollwin, TRUE, TRUE, 0); | ||
134 | gtk_widget_show(scrollwin); | ||
135 | |||
136 | viewport = gtk_viewport_new(NULL, NULL); | ||
137 | gtk_widget_show(viewport); | ||
138 | |||
139 | gtk_container_add(GTK_CONTAINER(scrollwin), viewport); | ||
140 | |||
141 | textview = gtk_text_view_new(); | ||
142 | buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview)); | ||
143 | gtk_text_buffer_set_text(buffer, statusstr->str, -1); | ||
144 | |||
145 | gtk_container_add(GTK_CONTAINER(viewport), textview); | ||
146 | gtk_widget_show(textview); | ||
147 | |||
148 | gtk_widget_set_size_request(GTK_WIDGET(dialog), | ||
149 | DIALOG_WIDTH, DIALOG_HEIGHT); | ||
150 | |||
151 | gtk_dialog_run(GTK_DIALOG(dialog)); | ||
152 | |||
153 | gtk_widget_destroy(dialog); | ||
154 | } | ||
155 | |||
156 | static gboolean | ||
157 | do_status_popup(GtkWidget *widget, GdkEventButton *event, gpointer data) | ||
158 | { | ||
159 | static GtkWidget *menu; | ||
160 | static GtkWidget *menu_status_display; | ||
161 | |||
162 | if (!menu) { | ||
163 | menu = gtk_menu_new(); | ||
164 | menu_status_display = gtk_menu_item_new_with_label("Display Status"); | ||
165 | gtk_widget_show(menu_status_display); | ||
166 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_status_display); | ||
167 | |||
168 | g_signal_connect_swapped (G_OBJECT (menu_status_display), "activate", | ||
169 | G_CALLBACK (status_display_clicked), | ||
170 | data); | ||
171 | } | ||
172 | |||
173 | gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, | ||
174 | gtk_get_current_event_time()); | ||
175 | |||
176 | return TRUE; | ||
177 | } | ||
178 | |||
179 | static gboolean | ||
180 | button_press_status(GtkWidget *widget, GdkEventButton *event, gpointer data) | ||
181 | { | ||
182 | if (event->button == 1) | ||
183 | return do_status_popup(widget, event, data); | ||
184 | |||
185 | return FALSE; | ||
186 | } | ||
187 | |||
188 | GtkWidget *trace_status_bar_new(void) | ||
189 | { | ||
190 | GtkWidget *eventbox; | ||
191 | |||
192 | statusbar = gtk_statusbar_new(); | ||
193 | |||
194 | statuspix = gtk_image_new_from_stock(GTK_STOCK_INFO, | ||
195 | GTK_ICON_SIZE_SMALL_TOOLBAR); | ||
196 | |||
197 | eventbox = gtk_event_box_new(); | ||
198 | gtk_container_add(GTK_CONTAINER(eventbox), statuspix); | ||
199 | gtk_widget_show(eventbox); | ||
200 | |||
201 | gtk_box_pack_end(GTK_BOX(statusbar), eventbox, FALSE, FALSE, 0); | ||
202 | |||
203 | if (statusstr) | ||
204 | gtk_widget_show(statuspix); | ||
205 | |||
206 | gtk_signal_connect(GTK_OBJECT(eventbox), "button_press_event", | ||
207 | (GtkSignalFunc) button_press_status, NULL); | ||
208 | |||
209 | return statusbar; | ||
210 | } | ||
211 | |||
212 | void trace_show_help(GtkWidget *window, const gchar *link, GError **error) | ||
213 | { | ||
214 | #if GTK_VERSION < CALC_GTK_VERSION(2,14,0) | ||
215 | trace_dialog(GTK_WINDOW(window), TRACE_GUI_WARNING, | ||
216 | "This version of GTK+ does not implement gtk_show_uri.\n" | ||
217 | "Please upgrade your GTK and recompile"); | ||
218 | #else | ||
219 | gtk_show_uri(gtk_widget_get_screen(GTK_WIDGET(window)), | ||
220 | link, | ||
221 | GDK_CURRENT_TIME, | ||
222 | error); | ||
223 | #endif | ||
224 | } | ||
225 | |||
226 | void trace_dialog(GtkWindow *parent, enum trace_dialog_type type, | ||
227 | gchar *message, ...) | ||
228 | { | ||
229 | GtkWidget *dialog; | ||
230 | GtkMessageType mtype; | ||
231 | gchar *str; | ||
232 | va_list ap; | ||
233 | |||
234 | switch (type) { | ||
235 | case TRACE_GUI_INFO: | ||
236 | mtype = GTK_MESSAGE_INFO; | ||
237 | break; | ||
238 | case TRACE_GUI_WARNING: | ||
239 | mtype = GTK_MESSAGE_WARNING; | ||
240 | break; | ||
241 | case TRACE_GUI_ERROR: | ||
242 | mtype = GTK_MESSAGE_ERROR; | ||
243 | break; | ||
244 | } | ||
245 | |||
246 | va_start(ap, message); | ||
247 | str = g_strdup_vprintf(message, ap); | ||
248 | va_end(ap); | ||
249 | |||
250 | dialog = gtk_message_dialog_new(parent, | ||
251 | GTK_DIALOG_DESTROY_WITH_PARENT, | ||
252 | mtype, | ||
253 | GTK_BUTTONS_CLOSE, | ||
254 | "%s", str); | ||
255 | g_free(str); | ||
256 | gtk_dialog_run(GTK_DIALOG(dialog)); | ||
257 | gtk_widget_destroy(dialog); | ||
258 | } | ||
259 | |||
260 | gchar *trace_get_file_dialog(const gchar *title) | ||
261 | { | ||
262 | GtkWidget *dialog; | ||
263 | gchar *filename = NULL; | ||
264 | |||
265 | dialog = gtk_file_chooser_dialog_new(title, | ||
266 | NULL, | ||
267 | GTK_FILE_CHOOSER_ACTION_OPEN, | ||
268 | GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | ||
269 | GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, | ||
270 | NULL); | ||
271 | if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) | ||
272 | filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); | ||
273 | |||
274 | gtk_widget_destroy(dialog); | ||
275 | |||
276 | return filename; | ||
277 | } | ||
diff --git a/trace-filter.c b/trace-filter.c index 15d59c0..b8fc76f 100644 --- a/trace-filter.c +++ b/trace-filter.c | |||
@@ -58,11 +58,6 @@ int id_cmp(const void *a, const void *b) | |||
58 | return 0; | 58 | return 0; |
59 | } | 59 | } |
60 | 60 | ||
61 | struct dialog_helper { | ||
62 | GtkWidget *dialog; | ||
63 | gpointer data; | ||
64 | }; | ||
65 | |||
66 | /** | 61 | /** |
67 | * trace_array_add - allocate and add an int to an array. | 62 | * trace_array_add - allocate and add an int to an array. |
68 | * @array: address of array to allocate | 63 | * @array: address of array to allocate |
@@ -97,14 +92,7 @@ struct event_combo_info { | |||
97 | GtkWidget *event_combo; | 92 | GtkWidget *event_combo; |
98 | GtkWidget *op_combo; | 93 | GtkWidget *op_combo; |
99 | GtkWidget *field_combo; | 94 | GtkWidget *field_combo; |
100 | }; | 95 | GtkWidget *entry; |
101 | |||
102 | struct adv_event_filter_helper { | ||
103 | trace_adv_filter_cb_func func; | ||
104 | GtkTreeView *view; | ||
105 | GtkWidget *entry; | ||
106 | struct event_combo_info combo_info; | ||
107 | gpointer data; | ||
108 | }; | 96 | }; |
109 | 97 | ||
110 | static GtkTreeModel *create_event_combo_model(struct pevent *pevent) | 98 | static GtkTreeModel *create_event_combo_model(struct pevent *pevent) |
@@ -319,7 +307,6 @@ static void event_combo_changed(GtkComboBox *combo, gpointer data) | |||
319 | static void insert_combo_text(struct event_combo_info *info, | 307 | static void insert_combo_text(struct event_combo_info *info, |
320 | GtkComboBox *combo) | 308 | GtkComboBox *combo) |
321 | { | 309 | { |
322 | struct adv_event_filter_helper *event_helper; | ||
323 | GtkTreeModel *model; | 310 | GtkTreeModel *model; |
324 | GtkTreeIter iter; | 311 | GtkTreeIter iter; |
325 | GtkWidget *entry; | 312 | GtkWidget *entry; |
@@ -337,8 +324,7 @@ static void insert_combo_text(struct event_combo_info *info, | |||
337 | 0, &text, | 324 | 0, &text, |
338 | -1); | 325 | -1); |
339 | 326 | ||
340 | event_helper = container_of(info, typeof(*event_helper), combo_info); | 327 | entry = info->entry; |
341 | entry = event_helper->entry; | ||
342 | 328 | ||
343 | pos = gtk_editable_get_position(GTK_EDITABLE(entry)); | 329 | pos = gtk_editable_get_position(GTK_EDITABLE(entry)); |
344 | gtk_editable_insert_text(GTK_EDITABLE(entry), text, strlen(text), &pos); | 330 | gtk_editable_insert_text(GTK_EDITABLE(entry), text, strlen(text), &pos); |
@@ -506,35 +492,6 @@ static gint *get_event_ids(GtkTreeView *treeview) | |||
506 | return ids; | 492 | return ids; |
507 | } | 493 | } |
508 | 494 | ||
509 | /* Callback for the clicked signal of the advanced filter button */ | ||
510 | static void | ||
511 | adv_filter_dialog_response (gpointer data, gint response_id) | ||
512 | { | ||
513 | struct dialog_helper *helper = data; | ||
514 | struct adv_event_filter_helper *event_helper = helper->data; | ||
515 | const gchar *text; | ||
516 | gint *event_ids; | ||
517 | |||
518 | switch (response_id) { | ||
519 | case GTK_RESPONSE_ACCEPT: | ||
520 | text = gtk_entry_get_text(GTK_ENTRY(event_helper->entry)); | ||
521 | event_ids = get_event_ids(event_helper->view); | ||
522 | event_helper->func(TRUE, text, event_ids, event_helper->data); | ||
523 | free(event_ids); | ||
524 | break; | ||
525 | case GTK_RESPONSE_REJECT: | ||
526 | event_helper->func(FALSE, NULL, NULL, event_helper->data); | ||
527 | break; | ||
528 | default: | ||
529 | break; | ||
530 | }; | ||
531 | |||
532 | gtk_widget_destroy(GTK_WIDGET(helper->dialog)); | ||
533 | |||
534 | g_free(event_helper); | ||
535 | g_free(helper); | ||
536 | } | ||
537 | |||
538 | static GtkTreeModel * | 495 | static GtkTreeModel * |
539 | create_tree_filter_model(struct tracecmd_input *handle, | 496 | create_tree_filter_model(struct tracecmd_input *handle, |
540 | struct event_filter *event_filter) | 497 | struct event_filter *event_filter) |
@@ -706,9 +663,8 @@ void trace_adv_filter_dialog(struct tracecmd_input *handle, | |||
706 | trace_adv_filter_cb_func func, | 663 | trace_adv_filter_cb_func func, |
707 | gpointer data) | 664 | gpointer data) |
708 | { | 665 | { |
666 | struct event_combo_info combo_info; | ||
709 | struct pevent *pevent; | 667 | struct pevent *pevent; |
710 | struct dialog_helper *helper; | ||
711 | struct adv_event_filter_helper *event_helper; | ||
712 | GtkWidget *dialog; | 668 | GtkWidget *dialog; |
713 | GtkWidget *hbox; | 669 | GtkWidget *hbox; |
714 | GtkWidget *label; | 670 | GtkWidget *label; |
@@ -716,13 +672,13 @@ void trace_adv_filter_dialog(struct tracecmd_input *handle, | |||
716 | GtkWidget *scrollwin; | 672 | GtkWidget *scrollwin; |
717 | GtkWidget *view; | 673 | GtkWidget *view; |
718 | GtkWidget *event_box; | 674 | GtkWidget *event_box; |
675 | const gchar *text; | ||
676 | gint *event_ids; | ||
677 | int result; | ||
719 | 678 | ||
720 | if (!handle) | 679 | if (!handle) |
721 | return; | 680 | return; |
722 | 681 | ||
723 | helper = g_malloc(sizeof(*helper)); | ||
724 | g_assert(helper); | ||
725 | |||
726 | /* --- Make dialog window --- */ | 682 | /* --- Make dialog window --- */ |
727 | 683 | ||
728 | dialog = gtk_dialog_new_with_buttons("Advanced Filters", | 684 | dialog = gtk_dialog_new_with_buttons("Advanced Filters", |
@@ -734,26 +690,11 @@ void trace_adv_filter_dialog(struct tracecmd_input *handle, | |||
734 | GTK_RESPONSE_REJECT, | 690 | GTK_RESPONSE_REJECT, |
735 | NULL); | 691 | NULL); |
736 | 692 | ||
737 | event_helper = g_new0(typeof(*event_helper), 1); | ||
738 | g_assert(event_helper); | ||
739 | |||
740 | helper->dialog = dialog; | ||
741 | helper->data = event_helper; | ||
742 | |||
743 | event_helper->func = func; | ||
744 | event_helper->data = data; | ||
745 | |||
746 | /* We can attach the Quit menu item to our exit function */ | ||
747 | g_signal_connect_swapped (dialog, "response", | ||
748 | G_CALLBACK (adv_filter_dialog_response), | ||
749 | (gpointer) helper); | ||
750 | |||
751 | scrollwin = gtk_scrolled_window_new(NULL, NULL); | 693 | scrollwin = gtk_scrolled_window_new(NULL, NULL); |
752 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), | 694 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), |
753 | GTK_POLICY_AUTOMATIC, | 695 | GTK_POLICY_AUTOMATIC, |
754 | GTK_POLICY_AUTOMATIC); | 696 | GTK_POLICY_AUTOMATIC); |
755 | view = create_adv_filter_view(handle, event_filter); | 697 | view = create_adv_filter_view(handle, event_filter); |
756 | event_helper->view = GTK_TREE_VIEW(view); | ||
757 | gtk_container_add(GTK_CONTAINER(scrollwin), view); | 698 | gtk_container_add(GTK_CONTAINER(scrollwin), view); |
758 | gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), scrollwin, TRUE, TRUE, 0); | 699 | gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), scrollwin, TRUE, TRUE, 0); |
759 | 700 | ||
@@ -769,9 +710,9 @@ void trace_adv_filter_dialog(struct tracecmd_input *handle, | |||
769 | 710 | ||
770 | pevent = tracecmd_get_pevent(handle); | 711 | pevent = tracecmd_get_pevent(handle); |
771 | 712 | ||
772 | event_helper->combo_info.pevent = pevent; | 713 | combo_info.pevent = pevent; |
773 | 714 | ||
774 | event_box = event_info_box(&event_helper->combo_info); | 715 | event_box = event_info_box(&combo_info); |
775 | gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), event_box, FALSE, FALSE, 0); | 716 | gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), event_box, FALSE, FALSE, 0); |
776 | gtk_widget_show(event_box); | 717 | gtk_widget_show(event_box); |
777 | 718 | ||
@@ -787,23 +728,33 @@ void trace_adv_filter_dialog(struct tracecmd_input *handle, | |||
787 | gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0); | 728 | gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0); |
788 | gtk_widget_show(entry); | 729 | gtk_widget_show(entry); |
789 | 730 | ||
790 | event_helper->entry = entry; | 731 | combo_info.entry = entry; |
791 | 732 | ||
792 | gtk_widget_set_size_request(GTK_WIDGET(dialog), | 733 | gtk_widget_set_size_request(GTK_WIDGET(dialog), |
793 | TEXT_DIALOG_WIDTH, TEXT_DIALOG_HEIGHT); | 734 | TEXT_DIALOG_WIDTH, TEXT_DIALOG_HEIGHT); |
794 | 735 | ||
795 | gtk_widget_show_all(dialog); | 736 | gtk_widget_show_all(dialog); |
737 | |||
738 | result = gtk_dialog_run(GTK_DIALOG(dialog)); | ||
739 | switch (result) { | ||
740 | case GTK_RESPONSE_ACCEPT: | ||
741 | text = gtk_entry_get_text(GTK_ENTRY(entry)); | ||
742 | event_ids = get_event_ids(GTK_TREE_VIEW(view)); | ||
743 | func(TRUE, text, event_ids, data); | ||
744 | free(event_ids); | ||
745 | break; | ||
746 | case GTK_RESPONSE_REJECT: | ||
747 | func(FALSE, NULL, NULL, data); | ||
748 | break; | ||
749 | default: | ||
750 | break; | ||
751 | }; | ||
752 | |||
753 | gtk_widget_destroy(dialog); | ||
796 | } | 754 | } |
797 | 755 | ||
798 | /* --- task list dialog --- */ | 756 | /* --- task list dialog --- */ |
799 | 757 | ||
800 | struct task_helper { | ||
801 | trace_task_cb_func func; | ||
802 | GtkTreeView *view; | ||
803 | gboolean start; | ||
804 | gpointer data; | ||
805 | }; | ||
806 | |||
807 | enum { | 758 | enum { |
808 | TASK_COL_SELECT, | 759 | TASK_COL_SELECT, |
809 | TASK_COL_PID, | 760 | TASK_COL_PID, |
@@ -850,35 +801,6 @@ static void get_tasks(GtkTreeView *treeview, | |||
850 | *non_selected = non_pids; | 801 | *non_selected = non_pids; |
851 | } | 802 | } |
852 | 803 | ||
853 | /* Callback for the clicked signal of the task filter button */ | ||
854 | static void | ||
855 | task_dialog_response (gpointer data, gint response_id) | ||
856 | { | ||
857 | struct dialog_helper *helper = data; | ||
858 | struct task_helper *event_helper = helper->data; | ||
859 | gint *selected; | ||
860 | gint *non_select; | ||
861 | |||
862 | switch (response_id) { | ||
863 | case GTK_RESPONSE_ACCEPT: | ||
864 | get_tasks(event_helper->view, &selected, &non_select); | ||
865 | event_helper->func(TRUE, selected, non_select, event_helper->data); | ||
866 | free(selected); | ||
867 | free(non_select); | ||
868 | break; | ||
869 | case GTK_RESPONSE_REJECT: | ||
870 | event_helper->func(FALSE, NULL, NULL, event_helper->data); | ||
871 | break; | ||
872 | default: | ||
873 | break; | ||
874 | }; | ||
875 | |||
876 | gtk_widget_destroy(GTK_WIDGET(helper->dialog)); | ||
877 | |||
878 | g_free(event_helper); | ||
879 | g_free(helper); | ||
880 | } | ||
881 | |||
882 | static GtkTreeModel * | 804 | static GtkTreeModel * |
883 | create_task_model(struct tracecmd_input *handle, | 805 | create_task_model(struct tracecmd_input *handle, |
884 | gint *tasks, | 806 | gint *tasks, |
@@ -960,14 +882,14 @@ create_task_model(struct tracecmd_input *handle, | |||
960 | 882 | ||
961 | static void task_cursor_changed(gpointer data, GtkTreeView *treeview) | 883 | static void task_cursor_changed(gpointer data, GtkTreeView *treeview) |
962 | { | 884 | { |
963 | struct task_helper *event_helper = data; | 885 | gboolean *start = data; |
964 | GtkTreeModel *model; | 886 | GtkTreeModel *model; |
965 | GtkTreePath *path; | 887 | GtkTreePath *path; |
966 | GtkTreeIter iter; | 888 | GtkTreeIter iter; |
967 | gboolean active; | 889 | gboolean active; |
968 | 890 | ||
969 | if (!event_helper->start) { | 891 | if (!*start) { |
970 | event_helper->start = TRUE; | 892 | *start = TRUE; |
971 | return; | 893 | return; |
972 | } | 894 | } |
973 | 895 | ||
@@ -999,7 +921,7 @@ static void task_cursor_changed(gpointer data, GtkTreeView *treeview) | |||
999 | static GtkWidget * | 921 | static GtkWidget * |
1000 | create_task_view(struct tracecmd_input *handle, | 922 | create_task_view(struct tracecmd_input *handle, |
1001 | gint *tasks, gint *selected, | 923 | gint *tasks, gint *selected, |
1002 | struct task_helper *event_helper) | 924 | gboolean *start) |
1003 | { | 925 | { |
1004 | GtkTreeViewColumn *col; | 926 | GtkTreeViewColumn *col; |
1005 | GtkCellRenderer *renderer; | 927 | GtkCellRenderer *renderer; |
@@ -1061,7 +983,7 @@ create_task_view(struct tracecmd_input *handle, | |||
1061 | 983 | ||
1062 | g_signal_connect_swapped (view, "cursor-changed", | 984 | g_signal_connect_swapped (view, "cursor-changed", |
1063 | G_CALLBACK (task_cursor_changed), | 985 | G_CALLBACK (task_cursor_changed), |
1064 | (gpointer) event_helper); | 986 | (gpointer)start); |
1065 | 987 | ||
1066 | return view; | 988 | return view; |
1067 | } | 989 | } |
@@ -1080,14 +1002,12 @@ void trace_task_dialog(struct tracecmd_input *handle, | |||
1080 | trace_task_cb_func func, | 1002 | trace_task_cb_func func, |
1081 | gpointer data) | 1003 | gpointer data) |
1082 | { | 1004 | { |
1083 | struct dialog_helper *helper; | ||
1084 | struct task_helper *event_helper; | ||
1085 | GtkWidget *dialog; | 1005 | GtkWidget *dialog; |
1086 | GtkWidget *scrollwin; | 1006 | GtkWidget *scrollwin; |
1087 | GtkWidget *view; | 1007 | GtkWidget *view; |
1088 | 1008 | gboolean start = FALSE; | |
1089 | helper = g_malloc(sizeof(*helper)); | 1009 | gint *non_select; |
1090 | g_assert(helper); | 1010 | int result; |
1091 | 1011 | ||
1092 | /* --- Make dialog window --- */ | 1012 | /* --- Make dialog window --- */ |
1093 | 1013 | ||
@@ -1100,27 +1020,11 @@ void trace_task_dialog(struct tracecmd_input *handle, | |||
1100 | GTK_RESPONSE_REJECT, | 1020 | GTK_RESPONSE_REJECT, |
1101 | NULL); | 1021 | NULL); |
1102 | 1022 | ||
1103 | event_helper = g_new0(typeof(*event_helper), 1); | ||
1104 | g_assert(event_helper); | ||
1105 | |||
1106 | helper->dialog = dialog; | ||
1107 | helper->data = event_helper; | ||
1108 | |||
1109 | event_helper->func = func; | ||
1110 | event_helper->data = data; | ||
1111 | event_helper->start = FALSE; | ||
1112 | |||
1113 | /* We can attach the Quit menu item to our exit function */ | ||
1114 | g_signal_connect_swapped (dialog, "response", | ||
1115 | G_CALLBACK (task_dialog_response), | ||
1116 | (gpointer) helper); | ||
1117 | |||
1118 | scrollwin = gtk_scrolled_window_new(NULL, NULL); | 1023 | scrollwin = gtk_scrolled_window_new(NULL, NULL); |
1119 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), | 1024 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), |
1120 | GTK_POLICY_AUTOMATIC, | 1025 | GTK_POLICY_AUTOMATIC, |
1121 | GTK_POLICY_AUTOMATIC); | 1026 | GTK_POLICY_AUTOMATIC); |
1122 | view = create_task_view(handle, tasks, selected, event_helper); | 1027 | view = create_task_view(handle, tasks, selected, &start); |
1123 | event_helper->view = GTK_TREE_VIEW(view); | ||
1124 | gtk_container_add(GTK_CONTAINER(scrollwin), view); | 1028 | gtk_container_add(GTK_CONTAINER(scrollwin), view); |
1125 | gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), scrollwin, TRUE, TRUE, 0); | 1029 | gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), scrollwin, TRUE, TRUE, 0); |
1126 | 1030 | ||
@@ -1128,6 +1032,25 @@ void trace_task_dialog(struct tracecmd_input *handle, | |||
1128 | DIALOG_WIDTH, DIALOG_HEIGHT); | 1032 | DIALOG_WIDTH, DIALOG_HEIGHT); |
1129 | 1033 | ||
1130 | gtk_widget_show_all(dialog); | 1034 | gtk_widget_show_all(dialog); |
1035 | |||
1036 | selected = NULL; | ||
1037 | |||
1038 | result = gtk_dialog_run(GTK_DIALOG(dialog)); | ||
1039 | switch (result) { | ||
1040 | case GTK_RESPONSE_ACCEPT: | ||
1041 | get_tasks(GTK_TREE_VIEW(view), &selected, &non_select); | ||
1042 | func(TRUE, selected, non_select, data); | ||
1043 | free(selected); | ||
1044 | free(non_select); | ||
1045 | break; | ||
1046 | case GTK_RESPONSE_REJECT: | ||
1047 | func(FALSE, NULL, NULL, data); | ||
1048 | break; | ||
1049 | default: | ||
1050 | break; | ||
1051 | }; | ||
1052 | |||
1053 | gtk_widget_destroy(dialog); | ||
1131 | } | 1054 | } |
1132 | 1055 | ||
1133 | enum { | 1056 | enum { |
@@ -1602,9 +1525,9 @@ static gint update_system_events(GtkTreeModel *model, | |||
1602 | return size; | 1525 | return size; |
1603 | } | 1526 | } |
1604 | 1527 | ||
1605 | static void accept_events(struct event_filter_helper *event_helper) | 1528 | static void accept_events(GtkTreeView *view, |
1529 | trace_filter_event_cb_func func, gpointer data) | ||
1606 | { | 1530 | { |
1607 | GtkTreeView *view = event_helper->view; | ||
1608 | GtkTreeModel *model; | 1531 | GtkTreeModel *model; |
1609 | GtkTreeIter iter; | 1532 | GtkTreeIter iter; |
1610 | gboolean active; | 1533 | gboolean active; |
@@ -1630,8 +1553,7 @@ static void accept_events(struct event_filter_helper *event_helper) | |||
1630 | &systems, systems_size, | 1553 | &systems, systems_size, |
1631 | &events, &events_size); | 1554 | &events, &events_size); |
1632 | 1555 | ||
1633 | event_helper->func(TRUE, active, systems, events, | 1556 | func(TRUE, active, systems, events, data); |
1634 | event_helper->data); | ||
1635 | 1557 | ||
1636 | if (systems) { | 1558 | if (systems) { |
1637 | for (i = 0; systems[i]; i++) | 1559 | for (i = 0; systems[i]; i++) |
@@ -1642,33 +1564,6 @@ static void accept_events(struct event_filter_helper *event_helper) | |||
1642 | g_free(events); | 1564 | g_free(events); |
1643 | } | 1565 | } |
1644 | 1566 | ||
1645 | /* Callback for the clicked signal of the Events filter button */ | ||
1646 | static void | ||
1647 | event_dialog_response (gpointer data, gint response_id) | ||
1648 | { | ||
1649 | struct dialog_helper *helper = data; | ||
1650 | struct event_filter_helper *event_helper = helper->data; | ||
1651 | |||
1652 | switch (response_id) { | ||
1653 | case GTK_RESPONSE_ACCEPT: | ||
1654 | printf("accept!\n"); | ||
1655 | accept_events(event_helper); | ||
1656 | break; | ||
1657 | case GTK_RESPONSE_REJECT: | ||
1658 | printf("reject!\n"); | ||
1659 | event_helper->func(FALSE, FALSE, NULL, NULL, | ||
1660 | event_helper->data); | ||
1661 | break; | ||
1662 | default: | ||
1663 | break; | ||
1664 | }; | ||
1665 | |||
1666 | gtk_widget_destroy(GTK_WIDGET(helper->dialog)); | ||
1667 | |||
1668 | g_free(event_helper); | ||
1669 | g_free(helper); | ||
1670 | } | ||
1671 | |||
1672 | static void filter_event_dialog(struct tracecmd_input *handle, | 1567 | static void filter_event_dialog(struct tracecmd_input *handle, |
1673 | struct event_filter *filter, | 1568 | struct event_filter *filter, |
1674 | gboolean all_events, | 1569 | gboolean all_events, |
@@ -1676,13 +1571,10 @@ static void filter_event_dialog(struct tracecmd_input *handle, | |||
1676 | trace_filter_event_cb_func func, | 1571 | trace_filter_event_cb_func func, |
1677 | gpointer data) | 1572 | gpointer data) |
1678 | { | 1573 | { |
1679 | struct dialog_helper *helper; | ||
1680 | struct event_filter_helper *event_helper; | ||
1681 | GtkWidget *dialog; | 1574 | GtkWidget *dialog; |
1682 | GtkWidget *scrollwin; | 1575 | GtkWidget *scrollwin; |
1683 | GtkWidget *view; | 1576 | GtkWidget *view; |
1684 | 1577 | int result; | |
1685 | helper = g_malloc(sizeof(*helper)); | ||
1686 | 1578 | ||
1687 | /* --- Make dialog window --- */ | 1579 | /* --- Make dialog window --- */ |
1688 | 1580 | ||
@@ -1695,26 +1587,11 @@ static void filter_event_dialog(struct tracecmd_input *handle, | |||
1695 | GTK_RESPONSE_REJECT, | 1587 | GTK_RESPONSE_REJECT, |
1696 | NULL); | 1588 | NULL); |
1697 | 1589 | ||
1698 | event_helper = g_new0(typeof(*event_helper), 1); | ||
1699 | g_assert(event_helper); | ||
1700 | |||
1701 | helper->dialog = dialog; | ||
1702 | helper->data = event_helper; | ||
1703 | |||
1704 | event_helper->func = func; | ||
1705 | event_helper->data = data; | ||
1706 | |||
1707 | /* We can attach the Quit menu item to our exit function */ | ||
1708 | g_signal_connect_swapped (dialog, "response", | ||
1709 | G_CALLBACK (event_dialog_response), | ||
1710 | (gpointer) helper); | ||
1711 | |||
1712 | scrollwin = gtk_scrolled_window_new(NULL, NULL); | 1590 | scrollwin = gtk_scrolled_window_new(NULL, NULL); |
1713 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), | 1591 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), |
1714 | GTK_POLICY_AUTOMATIC, | 1592 | GTK_POLICY_AUTOMATIC, |
1715 | GTK_POLICY_AUTOMATIC); | 1593 | GTK_POLICY_AUTOMATIC); |
1716 | view = create_event_list_view(handle, filter, all_events, systems, events); | 1594 | view = create_event_list_view(handle, filter, all_events, systems, events); |
1717 | event_helper->view = GTK_TREE_VIEW(view); | ||
1718 | 1595 | ||
1719 | gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), scrollwin, TRUE, TRUE, 0); | 1596 | gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), scrollwin, TRUE, TRUE, 0); |
1720 | gtk_container_add(GTK_CONTAINER(scrollwin), view); | 1597 | gtk_container_add(GTK_CONTAINER(scrollwin), view); |
@@ -1723,6 +1600,20 @@ static void filter_event_dialog(struct tracecmd_input *handle, | |||
1723 | DIALOG_WIDTH, DIALOG_HEIGHT); | 1600 | DIALOG_WIDTH, DIALOG_HEIGHT); |
1724 | 1601 | ||
1725 | gtk_widget_show_all(dialog); | 1602 | gtk_widget_show_all(dialog); |
1603 | |||
1604 | result = gtk_dialog_run(GTK_DIALOG(dialog)); | ||
1605 | switch (result) { | ||
1606 | case GTK_RESPONSE_ACCEPT: | ||
1607 | accept_events(GTK_TREE_VIEW(view), func, data); | ||
1608 | break; | ||
1609 | case GTK_RESPONSE_REJECT: | ||
1610 | func(FALSE, FALSE, NULL, NULL, data); | ||
1611 | break; | ||
1612 | default: | ||
1613 | break; | ||
1614 | }; | ||
1615 | |||
1616 | gtk_widget_destroy(dialog); | ||
1726 | } | 1617 | } |
1727 | 1618 | ||
1728 | /** | 1619 | /** |
@@ -1779,8 +1670,6 @@ struct cpu_filter_helper { | |||
1779 | guint64 *cpu_mask; | 1670 | guint64 *cpu_mask; |
1780 | GtkWidget **buttons; | 1671 | GtkWidget **buttons; |
1781 | int cpus; | 1672 | int cpus; |
1782 | trace_filter_cpu_cb_func func; | ||
1783 | gpointer data; | ||
1784 | }; | 1673 | }; |
1785 | 1674 | ||
1786 | static void destroy_cpu_helper(struct cpu_filter_helper *cpu_helper) | 1675 | static void destroy_cpu_helper(struct cpu_filter_helper *cpu_helper) |
@@ -1790,40 +1679,6 @@ static void destroy_cpu_helper(struct cpu_filter_helper *cpu_helper) | |||
1790 | g_free(cpu_helper); | 1679 | g_free(cpu_helper); |
1791 | } | 1680 | } |
1792 | 1681 | ||
1793 | /* Callback for the clicked signal of the CPUS filter button */ | ||
1794 | static void | ||
1795 | cpu_dialog_response (gpointer data, gint response_id) | ||
1796 | { | ||
1797 | struct dialog_helper *helper = data; | ||
1798 | struct cpu_filter_helper *cpu_helper = helper->data; | ||
1799 | guint64 *cpu_mask = NULL; | ||
1800 | |||
1801 | switch (response_id) { | ||
1802 | case GTK_RESPONSE_ACCEPT: | ||
1803 | |||
1804 | if (!cpu_helper->allcpus) { | ||
1805 | cpu_mask = cpu_helper->cpu_mask; | ||
1806 | cpu_helper->cpu_mask = NULL; | ||
1807 | } | ||
1808 | |||
1809 | cpu_helper->func(TRUE, cpu_helper->allcpus, cpu_mask, cpu_helper->data); | ||
1810 | break; | ||
1811 | |||
1812 | case GTK_RESPONSE_REJECT: | ||
1813 | cpu_helper->func(FALSE, FALSE, NULL, cpu_helper->data); | ||
1814 | break; | ||
1815 | default: | ||
1816 | break; | ||
1817 | }; | ||
1818 | |||
1819 | g_free(cpu_mask); | ||
1820 | |||
1821 | gtk_widget_destroy(GTK_WIDGET(helper->dialog)); | ||
1822 | |||
1823 | destroy_cpu_helper(helper->data); | ||
1824 | g_free(helper); | ||
1825 | } | ||
1826 | |||
1827 | #define CPU_ALL_CPUS_STR "All CPUs" | 1682 | #define CPU_ALL_CPUS_STR "All CPUs" |
1828 | 1683 | ||
1829 | void cpu_toggle(gpointer data, GtkWidget *widget) | 1684 | void cpu_toggle(gpointer data, GtkWidget *widget) |
@@ -1875,8 +1730,8 @@ void cpu_toggle(gpointer data, GtkWidget *widget) | |||
1875 | void trace_filter_cpu_dialog(gboolean all_cpus, guint64 *cpus_selected, gint cpus, | 1730 | void trace_filter_cpu_dialog(gboolean all_cpus, guint64 *cpus_selected, gint cpus, |
1876 | trace_filter_cpu_cb_func func, gpointer data) | 1731 | trace_filter_cpu_cb_func func, gpointer data) |
1877 | { | 1732 | { |
1878 | struct dialog_helper *helper; | ||
1879 | struct cpu_filter_helper *cpu_helper; | 1733 | struct cpu_filter_helper *cpu_helper; |
1734 | guint64 *cpu_mask = NULL; | ||
1880 | GtkWidget *dialog; | 1735 | GtkWidget *dialog; |
1881 | GtkWidget *scrollwin; | 1736 | GtkWidget *scrollwin; |
1882 | GtkWidget *viewport; | 1737 | GtkWidget *viewport; |
@@ -1888,15 +1743,11 @@ void trace_filter_cpu_dialog(gboolean all_cpus, guint64 *cpus_selected, gint cpu | |||
1888 | gint width, height; | 1743 | gint width, height; |
1889 | gint allset; | 1744 | gint allset; |
1890 | gint cpu; | 1745 | gint cpu; |
1891 | 1746 | int result; | |
1892 | helper = g_malloc(sizeof(*helper)); | ||
1893 | g_assert(helper != NULL); | ||
1894 | 1747 | ||
1895 | cpu_helper = g_new0(typeof(*cpu_helper), 1); | 1748 | cpu_helper = g_new0(typeof(*cpu_helper), 1); |
1896 | g_assert(cpu_helper != NULL); | 1749 | g_assert(cpu_helper != NULL); |
1897 | 1750 | ||
1898 | helper->data = cpu_helper; | ||
1899 | |||
1900 | /* --- Make dialog window --- */ | 1751 | /* --- Make dialog window --- */ |
1901 | 1752 | ||
1902 | dialog = gtk_dialog_new_with_buttons("Filter CPUS", | 1753 | dialog = gtk_dialog_new_with_buttons("Filter CPUS", |
@@ -1908,19 +1759,10 @@ void trace_filter_cpu_dialog(gboolean all_cpus, guint64 *cpus_selected, gint cpu | |||
1908 | GTK_RESPONSE_REJECT, | 1759 | GTK_RESPONSE_REJECT, |
1909 | NULL); | 1760 | NULL); |
1910 | 1761 | ||
1911 | helper->dialog = dialog; | ||
1912 | |||
1913 | cpu_helper->cpus = cpus; | 1762 | cpu_helper->cpus = cpus; |
1914 | cpu_helper->buttons = g_new0(GtkWidget *, cpus + 1); | 1763 | cpu_helper->buttons = g_new0(GtkWidget *, cpus + 1); |
1915 | g_assert(cpu_helper->buttons); | 1764 | g_assert(cpu_helper->buttons); |
1916 | 1765 | ||
1917 | cpu_helper->func = func; | ||
1918 | cpu_helper->data = data; | ||
1919 | |||
1920 | g_signal_connect_swapped (dialog, "response", | ||
1921 | G_CALLBACK (cpu_dialog_response), | ||
1922 | (gpointer) helper); | ||
1923 | |||
1924 | scrollwin = gtk_scrolled_window_new(NULL, NULL); | 1766 | scrollwin = gtk_scrolled_window_new(NULL, NULL); |
1925 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), | 1767 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), |
1926 | GTK_POLICY_AUTOMATIC, | 1768 | GTK_POLICY_AUTOMATIC, |
@@ -2012,6 +1854,31 @@ void trace_filter_cpu_dialog(gboolean all_cpus, guint64 *cpus_selected, gint cpu | |||
2012 | width, height); | 1854 | width, height); |
2013 | 1855 | ||
2014 | gtk_widget_show_all(dialog); | 1856 | gtk_widget_show_all(dialog); |
1857 | |||
1858 | result = gtk_dialog_run(GTK_DIALOG(dialog)); | ||
1859 | switch (result) { | ||
1860 | case GTK_RESPONSE_ACCEPT: | ||
1861 | |||
1862 | if (!cpu_helper->allcpus) { | ||
1863 | cpu_mask = cpu_helper->cpu_mask; | ||
1864 | cpu_helper->cpu_mask = NULL; | ||
1865 | } | ||
1866 | |||
1867 | func(TRUE, cpu_helper->allcpus, cpu_mask, data); | ||
1868 | break; | ||
1869 | |||
1870 | case GTK_RESPONSE_REJECT: | ||
1871 | func(FALSE, FALSE, NULL, data); | ||
1872 | break; | ||
1873 | default: | ||
1874 | break; | ||
1875 | }; | ||
1876 | |||
1877 | g_free(cpu_mask); | ||
1878 | |||
1879 | gtk_widget_destroy(dialog); | ||
1880 | |||
1881 | destroy_cpu_helper(cpu_helper); | ||
2015 | } | 1882 | } |
2016 | 1883 | ||
2017 | static void add_system_str(gchar ***systems, char *system, int count) | 1884 | static void add_system_str(gchar ***systems, char *system, int count) |
@@ -2158,3 +2025,236 @@ void trace_filter_convert_char_to_filter(struct event_filter *filter, | |||
2158 | 2025 | ||
2159 | pevent_filter_free(copy); | 2026 | pevent_filter_free(copy); |
2160 | } | 2027 | } |
2028 | |||
2029 | int trace_filter_save_events(struct tracecmd_xml_handle *handle, | ||
2030 | struct event_filter *filter) | ||
2031 | { | ||
2032 | struct event_format *event; | ||
2033 | char **systems; | ||
2034 | gint *event_ids; | ||
2035 | char *str; | ||
2036 | int i; | ||
2037 | |||
2038 | trace_filter_convert_filter_to_names(filter, &systems, | ||
2039 | &event_ids); | ||
2040 | |||
2041 | for (i = 0; systems && systems[i]; i++) | ||
2042 | tracecmd_xml_write_element(handle, "System", systems[i]); | ||
2043 | |||
2044 | for (i = 0; event_ids && event_ids[i] > 0; i++) { | ||
2045 | str = pevent_filter_make_string(filter, event_ids[i]); | ||
2046 | if (!str) | ||
2047 | continue; | ||
2048 | |||
2049 | event = pevent_find_event(filter->pevent, event_ids[i]); | ||
2050 | if (event) { | ||
2051 | |||
2052 | /* skip not filtered items */ | ||
2053 | if (strcmp(str, "FALSE") == 0) { | ||
2054 | free(str); | ||
2055 | continue; | ||
2056 | } | ||
2057 | |||
2058 | tracecmd_xml_start_sub_system(handle, "Event"); | ||
2059 | tracecmd_xml_write_element(handle, "System", event->system); | ||
2060 | tracecmd_xml_write_element(handle, "Name", event->name); | ||
2061 | /* If this is has an advanced filter, include that too */ | ||
2062 | if (strcmp(str, "TRUE") != 0) { | ||
2063 | tracecmd_xml_write_element(handle, "Advanced", | ||
2064 | str); | ||
2065 | } | ||
2066 | tracecmd_xml_end_sub_system(handle); | ||
2067 | } | ||
2068 | free(str); | ||
2069 | } | ||
2070 | |||
2071 | return 0; | ||
2072 | } | ||
2073 | |||
2074 | int trace_filter_save_tasks(struct tracecmd_xml_handle *handle, | ||
2075 | struct filter_task *filter) | ||
2076 | { | ||
2077 | char buffer[100]; | ||
2078 | int *pids; | ||
2079 | int i; | ||
2080 | |||
2081 | pids = filter_task_pids(filter); | ||
2082 | if (!pids) | ||
2083 | return -1; | ||
2084 | |||
2085 | for (i = 0; pids[i] >= 0; i++) { | ||
2086 | snprintf(buffer, 100, "%d", pids[i]); | ||
2087 | tracecmd_xml_write_element(handle, "Task", buffer); | ||
2088 | } | ||
2089 | |||
2090 | free(pids); | ||
2091 | |||
2092 | return 0; | ||
2093 | } | ||
2094 | |||
2095 | int trace_filter_load_events(struct event_filter *event_filter, | ||
2096 | struct tracecmd_xml_handle *handle, | ||
2097 | struct tracecmd_xml_system_node *node) | ||
2098 | { | ||
2099 | struct tracecmd_xml_system_node *child; | ||
2100 | const char *name; | ||
2101 | const char *system; | ||
2102 | const char *event; | ||
2103 | const char *value; | ||
2104 | char *buffer; | ||
2105 | |||
2106 | while (node) { | ||
2107 | name = tracecmd_xml_node_type(node); | ||
2108 | |||
2109 | if (strcmp(name, "System") == 0) { | ||
2110 | system = tracecmd_xml_node_value(handle, node); | ||
2111 | pevent_filter_add_filter_str(event_filter, | ||
2112 | system, NULL); | ||
2113 | } else if (strcmp(name, "Event") == 0) { | ||
2114 | system = NULL; | ||
2115 | event = NULL; | ||
2116 | value = NULL; | ||
2117 | child = tracecmd_xml_node_child(node); | ||
2118 | if (!child) | ||
2119 | return -1; | ||
2120 | do { | ||
2121 | name = tracecmd_xml_node_type(child); | ||
2122 | if (strcmp(name, "System") == 0) | ||
2123 | system = tracecmd_xml_node_value(handle, child); | ||
2124 | else if (strcmp(name, "Name") == 0) | ||
2125 | event = tracecmd_xml_node_value(handle, child); | ||
2126 | else if (strcmp(name, "Advanced") == 0) | ||
2127 | value = tracecmd_xml_node_value(handle, child); | ||
2128 | child = tracecmd_xml_node_next(child); | ||
2129 | } while (child); | ||
2130 | |||
2131 | if (event || system) { | ||
2132 | if (event && system) { | ||
2133 | if (value) { | ||
2134 | buffer = malloc_or_die(strlen(event) + | ||
2135 | strlen(system) + | ||
2136 | strlen(value) + 3); | ||
2137 | sprintf(buffer, "%s/%s:%s", | ||
2138 | system, event, value); | ||
2139 | } else { | ||
2140 | buffer = malloc_or_die(strlen(event) + | ||
2141 | strlen(system) + 2); | ||
2142 | sprintf(buffer, "%s/%s", | ||
2143 | system, event); | ||
2144 | } | ||
2145 | } else { | ||
2146 | if (!event) | ||
2147 | event = system; | ||
2148 | if (value) { | ||
2149 | buffer = malloc_or_die(strlen(event) + | ||
2150 | strlen(value) + 2); | ||
2151 | sprintf(buffer, "%s:%s", | ||
2152 | event, value); | ||
2153 | } else { | ||
2154 | buffer = malloc_or_die(strlen(event) + 1); | ||
2155 | sprintf(buffer, "%s", event); | ||
2156 | } | ||
2157 | } | ||
2158 | pevent_filter_add_filter_str(event_filter, | ||
2159 | buffer, NULL); | ||
2160 | free(buffer); | ||
2161 | } | ||
2162 | } | ||
2163 | |||
2164 | node = tracecmd_xml_node_next(node); | ||
2165 | } | ||
2166 | |||
2167 | return 0; | ||
2168 | } | ||
2169 | |||
2170 | int trace_filter_load_task_filter(struct filter_task *filter, | ||
2171 | struct tracecmd_xml_handle *handle, | ||
2172 | struct tracecmd_xml_system_node *node) | ||
2173 | { | ||
2174 | const char *name; | ||
2175 | const char *task; | ||
2176 | int pid; | ||
2177 | |||
2178 | if (!filter) | ||
2179 | return 0; | ||
2180 | |||
2181 | node = tracecmd_xml_node_child(node); | ||
2182 | |||
2183 | while (node) { | ||
2184 | name = tracecmd_xml_node_type(node); | ||
2185 | |||
2186 | if (strcmp(name, "Task") == 0) { | ||
2187 | task = tracecmd_xml_node_value(handle, node); | ||
2188 | pid = atoi(task); | ||
2189 | if (!filter_task_find_pid(filter, pid)) | ||
2190 | filter_task_add_pid(filter, pid); | ||
2191 | } | ||
2192 | node = tracecmd_xml_node_next(node); | ||
2193 | } | ||
2194 | |||
2195 | return 0; | ||
2196 | } | ||
2197 | |||
2198 | int trace_filter_load_filters(struct tracecmd_xml_handle *handle, | ||
2199 | const char *system_name, | ||
2200 | struct filter_task *task_filter, | ||
2201 | struct filter_task *hide_tasks) | ||
2202 | { | ||
2203 | struct tracecmd_xml_system *system; | ||
2204 | struct tracecmd_xml_system_node *syschild; | ||
2205 | const char *name; | ||
2206 | |||
2207 | system = tracecmd_xml_find_system(handle, system_name); | ||
2208 | if (!system) | ||
2209 | return -1; | ||
2210 | |||
2211 | |||
2212 | syschild = tracecmd_xml_system_node(system); | ||
2213 | if (!syschild) | ||
2214 | goto out_free_sys; | ||
2215 | |||
2216 | do { | ||
2217 | name = tracecmd_xml_node_type(syschild); | ||
2218 | |||
2219 | if (strcmp(name, "TaskFilter") == 0) | ||
2220 | trace_filter_load_task_filter(task_filter, handle, syschild); | ||
2221 | |||
2222 | else if (strcmp(name, "HideTasks") == 0) | ||
2223 | trace_filter_load_task_filter(hide_tasks, handle, syschild); | ||
2224 | |||
2225 | syschild = tracecmd_xml_node_next(syschild); | ||
2226 | } while (syschild); | ||
2227 | |||
2228 | tracecmd_xml_free_system(system); | ||
2229 | |||
2230 | return 0; | ||
2231 | |||
2232 | out_free_sys: | ||
2233 | tracecmd_xml_free_system(system); | ||
2234 | return -1; | ||
2235 | } | ||
2236 | |||
2237 | int trace_filter_save_filters(struct tracecmd_xml_handle *handle, | ||
2238 | const char *system_name, | ||
2239 | struct filter_task *task_filter, | ||
2240 | struct filter_task *hide_tasks) | ||
2241 | { | ||
2242 | |||
2243 | tracecmd_xml_start_system(handle, system_name); | ||
2244 | |||
2245 | if (task_filter && filter_task_count(task_filter)) { | ||
2246 | tracecmd_xml_start_sub_system(handle, "TaskFilter"); | ||
2247 | trace_filter_save_tasks(handle, task_filter); | ||
2248 | tracecmd_xml_end_sub_system(handle); | ||
2249 | } | ||
2250 | |||
2251 | if (hide_tasks && filter_task_count(hide_tasks)) { | ||
2252 | tracecmd_xml_start_sub_system(handle, "HideTasks"); | ||
2253 | trace_filter_save_tasks(handle, hide_tasks); | ||
2254 | tracecmd_xml_end_sub_system(handle); | ||
2255 | } | ||
2256 | |||
2257 | tracecmd_xml_end_system(handle); | ||
2258 | |||
2259 | return 0; | ||
2260 | } | ||
diff --git a/trace-filter.h b/trace-filter.h index c68e8f0..1800471 100644 --- a/trace-filter.h +++ b/trace-filter.h | |||
@@ -23,6 +23,8 @@ | |||
23 | 23 | ||
24 | #include <gtk/gtk.h> | 24 | #include <gtk/gtk.h> |
25 | 25 | ||
26 | #include "trace-xml.h" | ||
27 | |||
26 | struct event_filter_list { | 28 | struct event_filter_list { |
27 | struct event_filter_list *next; | 29 | struct event_filter_list *next; |
28 | struct event *event; | 30 | struct event *event; |
@@ -133,11 +135,31 @@ typedef void (*trace_filter_cpu_cb_func)(gboolean accept, | |||
133 | void trace_filter_cpu_dialog(gboolean all_cpus, guint64 *cpu_mask_selected, gint cpus, | 135 | void trace_filter_cpu_dialog(gboolean all_cpus, guint64 *cpu_mask_selected, gint cpus, |
134 | trace_filter_cpu_cb_func func, gpointer data); | 136 | trace_filter_cpu_cb_func func, gpointer data); |
135 | 137 | ||
138 | void trace_array_add(gint **array, gint *count, gint val); | ||
139 | |||
140 | /* save and load filters */ | ||
141 | int trace_filter_save_events(struct tracecmd_xml_handle *handle, | ||
142 | struct event_filter *filter); | ||
143 | int trace_filter_save_tasks(struct tracecmd_xml_handle *handle, | ||
144 | struct filter_task *filter); | ||
145 | int trace_filter_load_events(struct event_filter *event_filter, | ||
146 | struct tracecmd_xml_handle *handle, | ||
147 | struct tracecmd_xml_system_node *node); | ||
148 | int trace_filter_load_task_filter(struct filter_task *filter, | ||
149 | struct tracecmd_xml_handle *handle, | ||
150 | struct tracecmd_xml_system_node *node); | ||
151 | int trace_filter_load_filters(struct tracecmd_xml_handle *handle, | ||
152 | const char *system_name, | ||
153 | struct filter_task *task_filter, | ||
154 | struct filter_task *hide_tasks); | ||
155 | int trace_filter_save_filters(struct tracecmd_xml_handle *handle, | ||
156 | const char *system_name, | ||
157 | struct filter_task *task_filter, | ||
158 | struct filter_task *hide_tasks); | ||
159 | |||
136 | /* put here because there's no other place */ | 160 | /* put here because there's no other place */ |
137 | 161 | ||
138 | int str_cmp(const void *a, const void *b); | 162 | int str_cmp(const void *a, const void *b); |
139 | int id_cmp(const void *a, const void *b); | 163 | int id_cmp(const void *a, const void *b); |
140 | 164 | ||
141 | void trace_array_add(gint **array, gint *count, gint val); | ||
142 | |||
143 | #endif /* _TRACE_FILTER_H */ | 165 | #endif /* _TRACE_FILTER_H */ |
diff --git a/trace-graph-main.c b/trace-graph-main.c index b373dd2..b4f05fb 100644 --- a/trace-graph-main.c +++ b/trace-graph-main.c | |||
@@ -29,6 +29,9 @@ | |||
29 | #include "trace-cmd.h" | 29 | #include "trace-cmd.h" |
30 | #include "trace-graph.h" | 30 | #include "trace-graph.h" |
31 | #include "trace-filter.h" | 31 | #include "trace-filter.h" |
32 | #include "trace-gui.h" | ||
33 | |||
34 | #include "version.h" | ||
32 | 35 | ||
33 | #define version "0.1.1" | 36 | #define version "0.1.1" |
34 | 37 | ||
@@ -52,26 +55,19 @@ load_clicked (gpointer data) | |||
52 | { | 55 | { |
53 | struct graph_info *ginfo = data; | 56 | struct graph_info *ginfo = data; |
54 | struct tracecmd_input *handle; | 57 | struct tracecmd_input *handle; |
55 | GtkWidget *dialog; | ||
56 | gchar *filename; | 58 | gchar *filename; |
57 | 59 | ||
58 | dialog = gtk_file_chooser_dialog_new("Load File", | 60 | filename = trace_get_file_dialog("Load File"); |
59 | NULL, | 61 | if (!filename) |
60 | GTK_FILE_CHOOSER_ACTION_OPEN, | 62 | return; |
61 | GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | 63 | |
62 | GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, | 64 | handle = tracecmd_open(filename); |
63 | NULL); | 65 | if (handle) { |
64 | if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { | 66 | trace_graph_load_handle(ginfo, handle); |
65 | filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); | 67 | /* Free handle when freeing graph */ |
66 | handle = tracecmd_open(filename); | 68 | tracecmd_close(handle); |
67 | if (handle) { | ||
68 | trace_graph_load_handle(ginfo, handle); | ||
69 | /* Free handle when freeing graph */ | ||
70 | tracecmd_close(handle); | ||
71 | } | ||
72 | g_free(filename); | ||
73 | } | 69 | } |
74 | gtk_widget_destroy(dialog); | 70 | g_free(filename); |
75 | } | 71 | } |
76 | 72 | ||
77 | /* Callback for the clicked signal of the Exit button */ | 73 | /* Callback for the clicked signal of the Exit button */ |
@@ -158,6 +154,60 @@ plot_tasks_clicked (gpointer data) | |||
158 | free(selected); | 154 | free(selected); |
159 | } | 155 | } |
160 | 156 | ||
157 | /* Callback for the clicked signal of the Load Filters button */ | ||
158 | static void | ||
159 | load_filters_clicked (gpointer data) | ||
160 | { | ||
161 | struct graph_info *ginfo = data; | ||
162 | struct tracecmd_xml_handle *handle; | ||
163 | gchar *filename; | ||
164 | |||
165 | filename = trace_get_file_dialog("Load Filters"); | ||
166 | if (!filename) | ||
167 | return; | ||
168 | |||
169 | handle = tracecmd_xml_open(filename); | ||
170 | if (!handle) | ||
171 | warning("Could not open %s", filename); | ||
172 | g_free(filename); | ||
173 | |||
174 | trace_filter_load_filters(handle, | ||
175 | "GraphTaskFilter", | ||
176 | ginfo->task_filter, | ||
177 | ginfo->hide_tasks); | ||
178 | |||
179 | trace_graph_load_filters(ginfo, handle); | ||
180 | |||
181 | tracecmd_xml_close(handle); | ||
182 | } | ||
183 | |||
184 | /* Callback for the clicked signal of the Save Filters button */ | ||
185 | static void | ||
186 | save_filters_clicked (gpointer data) | ||
187 | { | ||
188 | struct graph_info *ginfo = data; | ||
189 | struct tracecmd_xml_handle *handle; | ||
190 | gchar *filename; | ||
191 | |||
192 | filename = trace_get_file_dialog("Save Filters"); | ||
193 | if (!filename) | ||
194 | return; | ||
195 | |||
196 | handle = tracecmd_xml_create(filename, VERSION_STRING); | ||
197 | if (!handle) | ||
198 | warning("Could not create %s", filename); | ||
199 | g_free(filename); | ||
200 | |||
201 | trace_filter_save_filters(handle, | ||
202 | "GraphTaskFilter", | ||
203 | ginfo->task_filter, | ||
204 | ginfo->hide_tasks); | ||
205 | |||
206 | trace_graph_save_filters(ginfo, handle); | ||
207 | |||
208 | tracecmd_xml_close(handle); | ||
209 | } | ||
210 | |||
161 | void trace_graph(int argc, char **argv) | 211 | void trace_graph(int argc, char **argv) |
162 | { | 212 | { |
163 | struct tracecmd_input *handle = NULL; | 213 | struct tracecmd_input *handle = NULL; |
@@ -170,6 +220,7 @@ void trace_graph(int argc, char **argv) | |||
170 | GtkWidget *menu_item; | 220 | GtkWidget *menu_item; |
171 | GtkWidget *sub_item; | 221 | GtkWidget *sub_item; |
172 | GtkWidget *widget; | 222 | GtkWidget *widget; |
223 | GtkWidget *statusbar; | ||
173 | int c; | 224 | int c; |
174 | int ret; | 225 | int ret; |
175 | 226 | ||
@@ -215,6 +266,8 @@ void trace_graph(int argc, char **argv) | |||
215 | 266 | ||
216 | window = gtk_window_new(GTK_WINDOW_TOPLEVEL); | 267 | window = gtk_window_new(GTK_WINDOW_TOPLEVEL); |
217 | 268 | ||
269 | trace_dialog_register_window(window); | ||
270 | |||
218 | /* --- Top Level Vbox --- */ | 271 | /* --- Top Level Vbox --- */ |
219 | 272 | ||
220 | vbox = gtk_vbox_new(FALSE, 0); | 273 | vbox = gtk_vbox_new(FALSE, 0); |
@@ -253,6 +306,35 @@ void trace_graph(int argc, char **argv) | |||
253 | gtk_widget_show(sub_item); | 306 | gtk_widget_show(sub_item); |
254 | 307 | ||
255 | 308 | ||
309 | /* --- File - Load Filter Option --- */ | ||
310 | |||
311 | sub_item = gtk_menu_item_new_with_label("Load filters"); | ||
312 | |||
313 | /* Add them to the menu */ | ||
314 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); | ||
315 | |||
316 | g_signal_connect_swapped (G_OBJECT (sub_item), "activate", | ||
317 | G_CALLBACK (load_filters_clicked), | ||
318 | (gpointer) ginfo); | ||
319 | |||
320 | /* We do need to show menu items */ | ||
321 | gtk_widget_show(sub_item); | ||
322 | |||
323 | |||
324 | /* --- File - Save Filter Option --- */ | ||
325 | |||
326 | sub_item = gtk_menu_item_new_with_label("Save filters"); | ||
327 | |||
328 | /* Add them to the menu */ | ||
329 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); | ||
330 | |||
331 | g_signal_connect_swapped (G_OBJECT (sub_item), "activate", | ||
332 | G_CALLBACK (save_filters_clicked), | ||
333 | (gpointer) ginfo); | ||
334 | |||
335 | /* We do need to show menu items */ | ||
336 | gtk_widget_show(sub_item); | ||
337 | |||
256 | /* --- File - Quit Option --- */ | 338 | /* --- File - Quit Option --- */ |
257 | 339 | ||
258 | sub_item = gtk_menu_item_new_with_label("Quit"); | 340 | sub_item = gtk_menu_item_new_with_label("Quit"); |
@@ -380,6 +462,14 @@ void trace_graph(int argc, char **argv) | |||
380 | gtk_widget_show(widget); | 462 | gtk_widget_show(widget); |
381 | 463 | ||
382 | 464 | ||
465 | /* --- Set up Status Bar --- */ | ||
466 | |||
467 | statusbar = trace_status_bar_new(); | ||
468 | |||
469 | gtk_box_pack_start(GTK_BOX(vbox), statusbar, FALSE, FALSE, 0); | ||
470 | gtk_widget_show(statusbar); | ||
471 | |||
472 | |||
383 | /********************************************** | 473 | /********************************************** |
384 | * Main Window | 474 | * Main Window |
385 | **********************************************/ | 475 | **********************************************/ |
diff --git a/trace-graph.c b/trace-graph.c index 6ed7851..04e2439 100644 --- a/trace-graph.c +++ b/trace-graph.c | |||
@@ -548,6 +548,44 @@ void trace_graph_clear_tasks(struct graph_info *ginfo) | |||
548 | redraw_graph(ginfo); | 548 | redraw_graph(ginfo); |
549 | } | 549 | } |
550 | 550 | ||
551 | void trace_graph_update_filters(struct graph_info *ginfo, | ||
552 | struct filter_task *task_filter, | ||
553 | struct filter_task *hide_tasks) | ||
554 | { | ||
555 | /* Make sure the filter passed in is not the filter we use */ | ||
556 | if (task_filter != ginfo->task_filter) { | ||
557 | filter_task_hash_free(ginfo->task_filter); | ||
558 | ginfo->task_filter = filter_task_hash_copy(task_filter); | ||
559 | } | ||
560 | |||
561 | if (hide_tasks != ginfo->hide_tasks) { | ||
562 | filter_task_hash_free(ginfo->hide_tasks); | ||
563 | ginfo->hide_tasks = filter_task_hash_copy(hide_tasks); | ||
564 | } | ||
565 | |||
566 | if (ginfo->callbacks && ginfo->callbacks->filter) | ||
567 | ginfo->callbacks->filter(ginfo, ginfo->task_filter, | ||
568 | ginfo->hide_tasks); | ||
569 | |||
570 | if (ginfo->filter_enabled) | ||
571 | redraw_graph(ginfo); | ||
572 | |||
573 | if (filter_task_count(ginfo->task_filter) || | ||
574 | filter_task_count(ginfo->hide_tasks)) | ||
575 | ginfo->filter_available = 1; | ||
576 | else { | ||
577 | ginfo->filter_enabled = 0; | ||
578 | ginfo->filter_available = 0; | ||
579 | } | ||
580 | |||
581 | } | ||
582 | |||
583 | void trace_graph_refresh_filters(struct graph_info *ginfo) | ||
584 | { | ||
585 | trace_graph_update_filters(ginfo, ginfo->task_filter, | ||
586 | ginfo->hide_tasks); | ||
587 | } | ||
588 | |||
551 | static void | 589 | static void |
552 | filter_clear_tasks_clicked (gpointer data) | 590 | filter_clear_tasks_clicked (gpointer data) |
553 | { | 591 | { |
@@ -718,7 +756,7 @@ do_pop_up(GtkWidget *widget, GdkEventButton *event, gpointer data) | |||
718 | g_assert(text); | 756 | g_assert(text); |
719 | 757 | ||
720 | if (trace_graph_filter_task_find_pid(ginfo, pid)) | 758 | if (trace_graph_filter_task_find_pid(ginfo, pid)) |
721 | snprintf(text, len, "Remove %s-%d to filter", comm, pid); | 759 | snprintf(text, len, "Remove %s-%d from filter", comm, pid); |
722 | else | 760 | else |
723 | snprintf(text, len, "Add %s-%d to filter", comm, pid); | 761 | snprintf(text, len, "Add %s-%d to filter", comm, pid); |
724 | 762 | ||
@@ -728,9 +766,9 @@ do_pop_up(GtkWidget *widget, GdkEventButton *event, gpointer data) | |||
728 | text); | 766 | text); |
729 | 767 | ||
730 | if (trace_graph_hide_task_find_pid(ginfo, pid)) | 768 | if (trace_graph_hide_task_find_pid(ginfo, pid)) |
731 | snprintf(text, len, "Show %s-%d to filter", comm, pid); | 769 | snprintf(text, len, "Show %s-%d", comm, pid); |
732 | else | 770 | else |
733 | snprintf(text, len, "Hide %s-%d to filter", comm, pid); | 771 | snprintf(text, len, "Hide %s-%d", comm, pid); |
734 | 772 | ||
735 | gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_hide_task), | 773 | gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_hide_task), |
736 | text); | 774 | text); |
@@ -752,7 +790,7 @@ do_pop_up(GtkWidget *widget, GdkEventButton *event, gpointer data) | |||
752 | gtk_widget_set_sensitive(menu_filter_add_task, FALSE); | 790 | gtk_widget_set_sensitive(menu_filter_add_task, FALSE); |
753 | 791 | ||
754 | gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_hide_task), | 792 | gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_hide_task), |
755 | "Hide task to filter"); | 793 | "Hide task"); |
756 | gtk_widget_set_sensitive(menu_filter_hide_task, FALSE); | 794 | gtk_widget_set_sensitive(menu_filter_hide_task, FALSE); |
757 | 795 | ||
758 | gtk_widget_hide(menu_plot_task); | 796 | gtk_widget_hide(menu_plot_task); |
@@ -766,7 +804,25 @@ do_pop_up(GtkWidget *widget, GdkEventButton *event, gpointer data) | |||
766 | return TRUE; | 804 | return TRUE; |
767 | } | 805 | } |
768 | 806 | ||
769 | static void button_press(struct graph_info *ginfo, gint x, guint state) | 807 | static void draw_info_box(struct graph_info *ginfo, const gchar *buffer, |
808 | gint x, gint y); | ||
809 | |||
810 | static void stop_zoom_tip(struct graph_info *ginfo) | ||
811 | { | ||
812 | clear_info_box(ginfo); | ||
813 | } | ||
814 | |||
815 | static void show_zoom_tip(struct graph_info *ginfo, gint x, gint y) | ||
816 | { | ||
817 | clear_info_box(ginfo); | ||
818 | |||
819 | draw_info_box(ginfo, | ||
820 | "Click and hold left mouse and drag right to zoom in\n" | ||
821 | "Click and hold left mouse and drag left to zoom out", | ||
822 | x, y); | ||
823 | } | ||
824 | |||
825 | static void button_press(struct graph_info *ginfo, gint x, gint y, guint state) | ||
770 | { | 826 | { |
771 | ginfo->press_x = x; | 827 | ginfo->press_x = x; |
772 | ginfo->last_x = 0; | 828 | ginfo->last_x = 0; |
@@ -785,8 +841,10 @@ static void button_press(struct graph_info *ginfo, gint x, guint state) | |||
785 | clear_line(ginfo, convert_time_to_x(ginfo, ginfo->marka_time)); | 841 | clear_line(ginfo, convert_time_to_x(ginfo, ginfo->marka_time)); |
786 | update_marka(ginfo, x); | 842 | update_marka(ginfo, x); |
787 | } | 843 | } |
788 | } else | 844 | } else { |
789 | ginfo->zoom = TRUE; | 845 | ginfo->zoom = TRUE; |
846 | show_zoom_tip(ginfo, x, y); | ||
847 | } | ||
790 | 848 | ||
791 | return; | 849 | return; |
792 | } | 850 | } |
@@ -807,6 +865,7 @@ button_press_event(GtkWidget *widget, GdkEventButton *event, gpointer data) | |||
807 | 865 | ||
808 | /* check for double click */ | 866 | /* check for double click */ |
809 | if (event->type == GDK_2BUTTON_PRESS) { | 867 | if (event->type == GDK_2BUTTON_PRESS) { |
868 | stop_zoom_tip(ginfo); | ||
810 | if (ginfo->line_active) { | 869 | if (ginfo->line_active) { |
811 | ginfo->line_active = FALSE; | 870 | ginfo->line_active = FALSE; |
812 | clear_line(ginfo, ginfo->last_x); | 871 | clear_line(ginfo, ginfo->last_x); |
@@ -826,7 +885,7 @@ button_press_event(GtkWidget *widget, GdkEventButton *event, gpointer data) | |||
826 | return TRUE; | 885 | return TRUE; |
827 | } | 886 | } |
828 | 887 | ||
829 | button_press(ginfo, event->x, event->state); | 888 | button_press(ginfo, event->x, event->y, event->state); |
830 | 889 | ||
831 | return TRUE; | 890 | return TRUE; |
832 | } | 891 | } |
@@ -839,6 +898,9 @@ static void motion_plot(struct graph_info *ginfo, gint x, gint y) | |||
839 | { | 898 | { |
840 | struct graph_plot *plot; | 899 | struct graph_plot *plot; |
841 | 900 | ||
901 | if (ginfo->zoom) | ||
902 | stop_zoom_tip(ginfo); | ||
903 | |||
842 | if (!ginfo->curr_pixmap) | 904 | if (!ginfo->curr_pixmap) |
843 | return; | 905 | return; |
844 | 906 | ||
@@ -876,7 +938,7 @@ info_button_press_event(GtkWidget *widget, GdkEventButton *event, gpointer data) | |||
876 | if (event->type == GDK_2BUTTON_PRESS) | 938 | if (event->type == GDK_2BUTTON_PRESS) |
877 | return FALSE; | 939 | return FALSE; |
878 | 940 | ||
879 | button_press(ginfo, gtk_adjustment_get_value(ginfo->hadj), event->state); | 941 | button_press(ginfo, gtk_adjustment_get_value(ginfo->hadj), event->y, event->state); |
880 | 942 | ||
881 | return FALSE; | 943 | return FALSE; |
882 | } | 944 | } |
@@ -1500,7 +1562,8 @@ static void button_release(struct graph_info *ginfo, gint x) | |||
1500 | ginfo->show_marka = TRUE; | 1562 | ginfo->show_marka = TRUE; |
1501 | ginfo->show_markb = TRUE; | 1563 | ginfo->show_markb = TRUE; |
1502 | update_markb(ginfo, x); | 1564 | update_markb(ginfo, x); |
1503 | } | 1565 | } else |
1566 | stop_zoom_tip(ginfo); | ||
1504 | 1567 | ||
1505 | clear_line(ginfo, ginfo->last_x); | 1568 | clear_line(ginfo, ginfo->last_x); |
1506 | clear_line(ginfo, ginfo->press_x); | 1569 | clear_line(ginfo, ginfo->press_x); |
@@ -2395,6 +2458,132 @@ int trace_graph_load_handle(struct graph_info *ginfo, | |||
2395 | return 0; | 2458 | return 0; |
2396 | } | 2459 | } |
2397 | 2460 | ||
2461 | static int load_event_filter(struct graph_info *ginfo, | ||
2462 | struct tracecmd_xml_handle *handle, | ||
2463 | struct tracecmd_xml_system_node *node) | ||
2464 | { | ||
2465 | struct tracecmd_xml_system_node *child; | ||
2466 | struct event_filter *event_filter; | ||
2467 | const char *name; | ||
2468 | const char *value; | ||
2469 | |||
2470 | event_filter = ginfo->event_filter; | ||
2471 | |||
2472 | child = tracecmd_xml_node_child(node); | ||
2473 | name = tracecmd_xml_node_type(child); | ||
2474 | if (strcmp(name, "FilterType") != 0) | ||
2475 | return -1; | ||
2476 | |||
2477 | value = tracecmd_xml_node_value(handle, child); | ||
2478 | /* Do nothing with all events enabled */ | ||
2479 | if (strcmp(value, "all events") == 0) | ||
2480 | return 0; | ||
2481 | |||
2482 | node = tracecmd_xml_node_next(child); | ||
2483 | if (!node) | ||
2484 | return -1; | ||
2485 | |||
2486 | pevent_filter_clear_trivial(event_filter, FILTER_TRIVIAL_BOTH); | ||
2487 | ginfo->all_events = FALSE; | ||
2488 | |||
2489 | trace_filter_load_events(event_filter, handle, node); | ||
2490 | |||
2491 | return 0; | ||
2492 | } | ||
2493 | |||
2494 | int trace_graph_load_filters(struct graph_info *ginfo, | ||
2495 | struct tracecmd_xml_handle *handle) | ||
2496 | { | ||
2497 | struct tracecmd_xml_system *system; | ||
2498 | struct tracecmd_xml_system_node *syschild; | ||
2499 | const char *name; | ||
2500 | |||
2501 | if (filter_task_count(ginfo->task_filter) || | ||
2502 | filter_task_count(ginfo->hide_tasks)) | ||
2503 | ginfo->filter_available = 1; | ||
2504 | else | ||
2505 | ginfo->filter_available = 0; | ||
2506 | |||
2507 | system = tracecmd_xml_find_system(handle, "TraceGraph"); | ||
2508 | if (!system) | ||
2509 | return -1; | ||
2510 | |||
2511 | syschild = tracecmd_xml_system_node(system); | ||
2512 | if (!syschild) | ||
2513 | goto out_free_sys; | ||
2514 | |||
2515 | do { | ||
2516 | name = tracecmd_xml_node_type(syschild); | ||
2517 | |||
2518 | if (strcmp(name, "EventFilter") == 0) | ||
2519 | load_event_filter(ginfo, handle, syschild); | ||
2520 | |||
2521 | syschild = tracecmd_xml_node_next(syschild); | ||
2522 | } while (syschild); | ||
2523 | |||
2524 | if (filter_task_count(ginfo->task_filter) || | ||
2525 | filter_task_count(ginfo->hide_tasks)) | ||
2526 | ginfo->filter_available = 1; | ||
2527 | else | ||
2528 | ginfo->filter_available = 0; | ||
2529 | |||
2530 | tracecmd_xml_free_system(system); | ||
2531 | |||
2532 | trace_graph_refresh(ginfo); | ||
2533 | |||
2534 | return 0; | ||
2535 | |||
2536 | out_free_sys: | ||
2537 | tracecmd_xml_free_system(system); | ||
2538 | if (ginfo->filter_enabled) | ||
2539 | trace_graph_refresh(ginfo); | ||
2540 | |||
2541 | return -1; | ||
2542 | } | ||
2543 | |||
2544 | int trace_graph_save_filters(struct graph_info *ginfo, | ||
2545 | struct tracecmd_xml_handle *handle) | ||
2546 | { | ||
2547 | struct event_filter *event_filter; | ||
2548 | |||
2549 | tracecmd_xml_start_system(handle, "TraceGraph"); | ||
2550 | |||
2551 | event_filter = ginfo->event_filter; | ||
2552 | |||
2553 | tracecmd_xml_start_sub_system(handle, "EventFilter"); | ||
2554 | |||
2555 | if (ginfo->all_events || !event_filter) | ||
2556 | tracecmd_xml_write_element(handle, "FilterType", "all events"); | ||
2557 | else { | ||
2558 | tracecmd_xml_write_element(handle, "FilterType", "filter"); | ||
2559 | trace_filter_save_events(handle, event_filter); | ||
2560 | } | ||
2561 | |||
2562 | tracecmd_xml_end_sub_system(handle); | ||
2563 | |||
2564 | tracecmd_xml_end_system(handle); | ||
2565 | |||
2566 | return 0; | ||
2567 | } | ||
2568 | |||
2569 | static void set_label_a(GtkWidget *widget) | ||
2570 | { | ||
2571 | gtk_widget_set_tooltip_text(widget, "Click left mouse on graph\n" | ||
2572 | "to set Marker A"); | ||
2573 | } | ||
2574 | |||
2575 | static void set_label_b(GtkWidget *widget) | ||
2576 | { | ||
2577 | gtk_widget_set_tooltip_text(widget, "Shift and click left mouse on graph\n" | ||
2578 | "to set Marker B"); | ||
2579 | } | ||
2580 | |||
2581 | static void set_label_cursor(GtkWidget *widget) | ||
2582 | { | ||
2583 | gtk_widget_set_tooltip_text(widget, "Double click Left mouse on graph\n" | ||
2584 | "to set Cursor"); | ||
2585 | } | ||
2586 | |||
2398 | struct graph_info * | 2587 | struct graph_info * |
2399 | trace_graph_create_with_callbacks(struct tracecmd_input *handle, | 2588 | trace_graph_create_with_callbacks(struct tracecmd_input *handle, |
2400 | struct graph_callbacks *cbs) | 2589 | struct graph_callbacks *cbs) |
@@ -2454,11 +2643,13 @@ trace_graph_create_with_callbacks(struct tracecmd_input *handle, | |||
2454 | /* --- Cursor --- */ | 2643 | /* --- Cursor --- */ |
2455 | 2644 | ||
2456 | label = gtk_label_new("Cursor:"); | 2645 | label = gtk_label_new("Cursor:"); |
2646 | set_label_cursor(label); | ||
2457 | gtk_table_attach(GTK_TABLE(table), label, 4, 5, 0, 1, GTK_EXPAND, GTK_EXPAND, 3, 3); | 2647 | gtk_table_attach(GTK_TABLE(table), label, 4, 5, 0, 1, GTK_EXPAND, GTK_EXPAND, 3, 3); |
2458 | gtk_widget_show(label); | 2648 | gtk_widget_show(label); |
2459 | 2649 | ||
2460 | ginfo->cursor_label = gtk_label_new("0.0"); | 2650 | ginfo->cursor_label = gtk_label_new("0.0"); |
2461 | eventbox = gtk_event_box_new(); | 2651 | eventbox = gtk_event_box_new(); |
2652 | set_label_cursor(eventbox); | ||
2462 | gtk_widget_show(eventbox); | 2653 | gtk_widget_show(eventbox); |
2463 | gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, &color); | 2654 | gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, &color); |
2464 | gtk_container_add(GTK_CONTAINER(eventbox), ginfo->cursor_label); | 2655 | gtk_container_add(GTK_CONTAINER(eventbox), ginfo->cursor_label); |
@@ -2473,9 +2664,11 @@ trace_graph_create_with_callbacks(struct tracecmd_input *handle, | |||
2473 | gtk_widget_show(hbox); | 2664 | gtk_widget_show(hbox); |
2474 | 2665 | ||
2475 | label = gtk_label_new("Marker"); | 2666 | label = gtk_label_new("Marker"); |
2667 | set_label_a(label); | ||
2476 | gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); | 2668 | gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); |
2477 | gtk_widget_show(label); | 2669 | gtk_widget_show(label); |
2478 | 2670 | ||
2671 | |||
2479 | label = gtk_label_new("A:"); | 2672 | label = gtk_label_new("A:"); |
2480 | 2673 | ||
2481 | colorAB.red = 0; | 2674 | colorAB.red = 0; |
@@ -2483,6 +2676,7 @@ trace_graph_create_with_callbacks(struct tracecmd_input *handle, | |||
2483 | colorAB.blue = 0; | 2676 | colorAB.blue = 0; |
2484 | 2677 | ||
2485 | eventbox = gtk_event_box_new(); | 2678 | eventbox = gtk_event_box_new(); |
2679 | set_label_a(eventbox); | ||
2486 | gtk_widget_show(eventbox); | 2680 | gtk_widget_show(eventbox); |
2487 | gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, &colorAB); | 2681 | gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, &colorAB); |
2488 | gtk_container_add(GTK_CONTAINER(eventbox), label); | 2682 | gtk_container_add(GTK_CONTAINER(eventbox), label); |
@@ -2494,6 +2688,7 @@ trace_graph_create_with_callbacks(struct tracecmd_input *handle, | |||
2494 | 2688 | ||
2495 | ginfo->marka_label = gtk_label_new("0.0"); | 2689 | ginfo->marka_label = gtk_label_new("0.0"); |
2496 | eventbox = gtk_event_box_new(); | 2690 | eventbox = gtk_event_box_new(); |
2691 | set_label_a(eventbox); | ||
2497 | gtk_widget_show(eventbox); | 2692 | gtk_widget_show(eventbox); |
2498 | gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, &color); | 2693 | gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, &color); |
2499 | gtk_container_add(GTK_CONTAINER(eventbox), ginfo->marka_label); | 2694 | gtk_container_add(GTK_CONTAINER(eventbox), ginfo->marka_label); |
@@ -2508,6 +2703,7 @@ trace_graph_create_with_callbacks(struct tracecmd_input *handle, | |||
2508 | gtk_widget_show(hbox); | 2703 | gtk_widget_show(hbox); |
2509 | 2704 | ||
2510 | label = gtk_label_new("Marker"); | 2705 | label = gtk_label_new("Marker"); |
2706 | set_label_b(label); | ||
2511 | gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); | 2707 | gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); |
2512 | gtk_widget_show(label); | 2708 | gtk_widget_show(label); |
2513 | 2709 | ||
@@ -2518,6 +2714,7 @@ trace_graph_create_with_callbacks(struct tracecmd_input *handle, | |||
2518 | colorAB.blue = 0; | 2714 | colorAB.blue = 0; |
2519 | 2715 | ||
2520 | eventbox = gtk_event_box_new(); | 2716 | eventbox = gtk_event_box_new(); |
2717 | set_label_b(eventbox); | ||
2521 | gtk_widget_show(eventbox); | 2718 | gtk_widget_show(eventbox); |
2522 | gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, &colorAB); | 2719 | gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, &colorAB); |
2523 | gtk_container_add(GTK_CONTAINER(eventbox), label); | 2720 | gtk_container_add(GTK_CONTAINER(eventbox), label); |
@@ -2530,6 +2727,7 @@ trace_graph_create_with_callbacks(struct tracecmd_input *handle, | |||
2530 | 2727 | ||
2531 | ginfo->markb_label = gtk_label_new("0.0"); | 2728 | ginfo->markb_label = gtk_label_new("0.0"); |
2532 | eventbox = gtk_event_box_new(); | 2729 | eventbox = gtk_event_box_new(); |
2730 | set_label_b(eventbox); | ||
2533 | gtk_widget_show(eventbox); | 2731 | gtk_widget_show(eventbox); |
2534 | gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, &color); | 2732 | gtk_widget_modify_bg(eventbox, GTK_STATE_NORMAL, &color); |
2535 | gtk_container_add(GTK_CONTAINER(eventbox), ginfo->markb_label); | 2733 | gtk_container_add(GTK_CONTAINER(eventbox), ginfo->markb_label); |
diff --git a/trace-graph.h b/trace-graph.h index 91ae161..15ad4b2 100644 --- a/trace-graph.h +++ b/trace-graph.h | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <gtk/gtk.h> | 24 | #include <gtk/gtk.h> |
25 | #include "trace-cmd.h" | 25 | #include "trace-cmd.h" |
26 | #include "trace-hash.h" | 26 | #include "trace-hash.h" |
27 | #include "trace-xml.h" | ||
27 | 28 | ||
28 | struct graph_info; | 29 | struct graph_info; |
29 | 30 | ||
@@ -306,6 +307,15 @@ void trace_graph_copy_filter(struct graph_info *ginfo, | |||
306 | struct event_filter *event_filter); | 307 | struct event_filter *event_filter); |
307 | gint *trace_graph_task_list(struct graph_info *ginfo); | 308 | gint *trace_graph_task_list(struct graph_info *ginfo); |
308 | 309 | ||
310 | int trace_graph_load_filters(struct graph_info *ginfo, | ||
311 | struct tracecmd_xml_handle *handle); | ||
312 | int trace_graph_save_filters(struct graph_info *ginfo, | ||
313 | struct tracecmd_xml_handle *handle); | ||
314 | void trace_graph_update_filters(struct graph_info *ginfo, | ||
315 | struct filter_task *task_filter, | ||
316 | struct filter_task *hide_tasks); | ||
317 | void trace_graph_refresh_filters(struct graph_info *ginfo); | ||
318 | |||
309 | /* plots */ | 319 | /* plots */ |
310 | void trace_graph_plot_free(struct graph_info *ginfo); | 320 | void trace_graph_plot_free(struct graph_info *ginfo); |
311 | void trace_graph_plot_init(struct graph_info *ginfo); | 321 | void trace_graph_plot_init(struct graph_info *ginfo); |
diff --git a/trace-gui.h b/trace-gui.h new file mode 100644 index 0000000..a2d261f --- /dev/null +++ b/trace-gui.h | |||
@@ -0,0 +1,44 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> | ||
3 | * | ||
4 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; version 2 of the License (not later!) | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | * | ||
19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
20 | */ | ||
21 | #ifndef _TRACE_GUI | ||
22 | #define _TRACE_GUI | ||
23 | |||
24 | #include <gtk/gtk.h> | ||
25 | |||
26 | enum trace_dialog_type { | ||
27 | TRACE_GUI_INFO, | ||
28 | TRACE_GUI_WARNING, | ||
29 | TRACE_GUI_ERROR, | ||
30 | }; | ||
31 | |||
32 | GtkWidget *trace_status_bar_new(void); | ||
33 | |||
34 | void trace_dialog_register_window(GtkWidget *window); | ||
35 | |||
36 | void trace_show_help(GtkWidget *window, const gchar *link, GError **error); | ||
37 | |||
38 | void trace_dialog(GtkWindow *parent, enum trace_dialog_type type, | ||
39 | gchar *message, ...); | ||
40 | |||
41 | gchar *trace_get_file_dialog(const gchar *title); | ||
42 | |||
43 | |||
44 | #endif /* _TRACE_GUI */ | ||
diff --git a/trace-hash.c b/trace-hash.c index 1197913..ed53ee1 100644 --- a/trace-hash.c +++ b/trace-hash.c | |||
@@ -19,6 +19,7 @@ | |||
19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
20 | */ | 20 | */ |
21 | #include <stdio.h> | 21 | #include <stdio.h> |
22 | #include <stdlib.h> | ||
22 | #include <string.h> | 23 | #include <string.h> |
23 | #include <stdarg.h> | 24 | #include <stdarg.h> |
24 | 25 | ||
@@ -155,3 +156,65 @@ struct filter_task *filter_task_hash_copy(struct filter_task *hash) | |||
155 | 156 | ||
156 | return new_hash; | 157 | return new_hash; |
157 | } | 158 | } |
159 | |||
160 | int *filter_task_pids(struct filter_task *hash) | ||
161 | { | ||
162 | struct filter_task_item *task; | ||
163 | int *pids; | ||
164 | int count = 0; | ||
165 | int i; | ||
166 | |||
167 | if (!hash->count) | ||
168 | return NULL; | ||
169 | |||
170 | pids = malloc(sizeof(*pids) * (hash->count + 1)); | ||
171 | if (!pids) | ||
172 | return NULL; | ||
173 | |||
174 | for (i = 0; i < FILTER_TASK_HASH_SIZE; i++) { | ||
175 | task = hash->hash[i]; | ||
176 | while (task) { | ||
177 | pids[count++] = task->pid; | ||
178 | task = task->next; | ||
179 | } | ||
180 | } | ||
181 | pids[count] = -1; | ||
182 | |||
183 | return pids; | ||
184 | } | ||
185 | |||
186 | /** | ||
187 | * filter_task_compare - compare two task hashs to see if they are equal | ||
188 | * @hash1: one hash to compare | ||
189 | * @hash2: another hash to compare to @hash1 | ||
190 | * | ||
191 | * Returns 1 if the two hashes are the same, 0 otherwise. | ||
192 | */ | ||
193 | int filter_task_compare(struct filter_task *hash1, struct filter_task *hash2) | ||
194 | { | ||
195 | int *pids; | ||
196 | int ret = 0; | ||
197 | int i; | ||
198 | |||
199 | /* If counts don't match, then they obviously are not the same */ | ||
200 | if (hash1->count != hash2->count) | ||
201 | return 0; | ||
202 | |||
203 | /* If both hashes are empty, they are the same */ | ||
204 | if (!hash1->count && !hash2->count) | ||
205 | return 1; | ||
206 | |||
207 | /* Now compare the pids of one hash with the other */ | ||
208 | pids = filter_task_pids(hash1); | ||
209 | for (i = 0; pids[i] >= 0; i++) { | ||
210 | if (!filter_task_find_pid(hash2, pids[i])) | ||
211 | break; | ||
212 | } | ||
213 | |||
214 | if (pids[i] == -1) | ||
215 | ret = 1; | ||
216 | |||
217 | free(pids); | ||
218 | |||
219 | return ret; | ||
220 | } | ||
diff --git a/trace-hash.h b/trace-hash.h index 1aaa828..b5296eb 100644 --- a/trace-hash.h +++ b/trace-hash.h | |||
@@ -42,6 +42,8 @@ void filter_task_clear(struct filter_task *hash); | |||
42 | struct filter_task *filter_task_hash_alloc(void); | 42 | struct filter_task *filter_task_hash_alloc(void); |
43 | void filter_task_hash_free(struct filter_task *hash); | 43 | void filter_task_hash_free(struct filter_task *hash); |
44 | struct filter_task *filter_task_hash_copy(struct filter_task *hash); | 44 | struct filter_task *filter_task_hash_copy(struct filter_task *hash); |
45 | int *filter_task_pids(struct filter_task *hash); | ||
46 | int filter_task_compare(struct filter_task *hash1, struct filter_task *hash2); | ||
45 | 47 | ||
46 | static inline gint filter_task_count(struct filter_task *hash) | 48 | static inline gint filter_task_count(struct filter_task *hash) |
47 | { | 49 | { |
diff --git a/trace-util.c b/trace-util.c index 6605f1f..b8b6faf 100644 --- a/trace-util.c +++ b/trace-util.c | |||
@@ -38,8 +38,6 @@ | |||
38 | int tracecmd_disable_sys_plugins; | 38 | int tracecmd_disable_sys_plugins; |
39 | int tracecmd_disable_plugins; | 39 | int tracecmd_disable_plugins; |
40 | 40 | ||
41 | #define __weak __attribute__((weak)) | ||
42 | |||
43 | #define _STR(x) #x | 41 | #define _STR(x) #x |
44 | #define STR(x) _STR(x) | 42 | #define STR(x) _STR(x) |
45 | 43 | ||
@@ -53,71 +51,6 @@ struct plugin_list { | |||
53 | void *handle; | 51 | void *handle; |
54 | }; | 52 | }; |
55 | 53 | ||
56 | void __weak die(char *fmt, ...) | ||
57 | { | ||
58 | va_list ap; | ||
59 | int ret = errno; | ||
60 | |||
61 | if (errno) | ||
62 | perror("trace-cmd"); | ||
63 | else | ||
64 | ret = -1; | ||
65 | |||
66 | va_start(ap, fmt); | ||
67 | fprintf(stderr, " "); | ||
68 | vfprintf(stderr, fmt, ap); | ||
69 | va_end(ap); | ||
70 | |||
71 | fprintf(stderr, "\n"); | ||
72 | exit(ret); | ||
73 | } | ||
74 | |||
75 | void __weak warning(char *fmt, ...) | ||
76 | { | ||
77 | va_list ap; | ||
78 | |||
79 | if (errno) | ||
80 | perror("trace-cmd"); | ||
81 | errno = 0; | ||
82 | |||
83 | va_start(ap, fmt); | ||
84 | fprintf(stderr, " "); | ||
85 | vfprintf(stderr, fmt, ap); | ||
86 | va_end(ap); | ||
87 | |||
88 | fprintf(stderr, "\n"); | ||
89 | } | ||
90 | |||
91 | void __weak pr_stat(char *fmt, ...) | ||
92 | { | ||
93 | va_list ap; | ||
94 | |||
95 | va_start(ap, fmt); | ||
96 | vprintf(fmt, ap); | ||
97 | va_end(ap); | ||
98 | |||
99 | printf("\n"); | ||
100 | } | ||
101 | |||
102 | void __weak *malloc_or_die(unsigned int size) | ||
103 | { | ||
104 | void *data; | ||
105 | |||
106 | data = malloc(size); | ||
107 | if (!data) | ||
108 | die("malloc"); | ||
109 | return data; | ||
110 | } | ||
111 | |||
112 | int __weak bigendian(void) | ||
113 | { | ||
114 | unsigned char str[] = { 0x1, 0x2, 0x3, 0x4 }; | ||
115 | unsigned int *ptr; | ||
116 | |||
117 | ptr = (unsigned int *)str; | ||
118 | return *ptr == 0x01020304; | ||
119 | } | ||
120 | |||
121 | void parse_cmdlines(struct pevent *pevent, | 54 | void parse_cmdlines(struct pevent *pevent, |
122 | char *file, int size __unused) | 55 | char *file, int size __unused) |
123 | { | 56 | { |
diff --git a/trace-view-main.c b/trace-view-main.c index 52678ce..59d6838 100644 --- a/trace-view-main.c +++ b/trace-view-main.c | |||
@@ -28,6 +28,12 @@ | |||
28 | 28 | ||
29 | #include "trace-cmd.h" | 29 | #include "trace-cmd.h" |
30 | #include "trace-view.h" | 30 | #include "trace-view.h" |
31 | #include "trace-xml.h" | ||
32 | #include "trace-filter.h" | ||
33 | #include "trace-gui.h" | ||
34 | #include "trace-compat.h" | ||
35 | |||
36 | #include "version.h" | ||
31 | 37 | ||
32 | #define version "0.1.1" | 38 | #define version "0.1.1" |
33 | 39 | ||
@@ -38,8 +44,13 @@ | |||
38 | static char *input_file; | 44 | static char *input_file; |
39 | 45 | ||
40 | struct trace_tree_info { | 46 | struct trace_tree_info { |
41 | GtkWidget *trace_tree; | 47 | struct tracecmd_input *handle; |
42 | GtkWidget *spin; | 48 | GtkWidget *trace_tree; |
49 | GtkWidget *spin; | ||
50 | gint filter_enabled; | ||
51 | gint filter_task_selected; | ||
52 | struct filter_task *task_filter; | ||
53 | struct filter_task *hide_tasks; | ||
43 | }; | 54 | }; |
44 | 55 | ||
45 | void usage(char *prog) | 56 | void usage(char *prog) |
@@ -55,26 +66,76 @@ load_clicked (gpointer data) | |||
55 | { | 66 | { |
56 | struct trace_tree_info *info = data; | 67 | struct trace_tree_info *info = data; |
57 | struct tracecmd_input *handle; | 68 | struct tracecmd_input *handle; |
58 | GtkWidget *dialog; | ||
59 | gchar *filename; | 69 | gchar *filename; |
60 | 70 | ||
61 | dialog = gtk_file_chooser_dialog_new("Load File", | 71 | filename = trace_get_file_dialog("Load File"); |
62 | NULL, | 72 | if (!filename) |
63 | GTK_FILE_CHOOSER_ACTION_OPEN, | 73 | return; |
64 | GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | 74 | |
65 | GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, | 75 | handle = tracecmd_open(filename); |
66 | NULL); | 76 | if (handle) { |
67 | if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { | 77 | trace_view_reload(info->trace_tree, handle, info->spin); |
68 | filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); | 78 | /* Free handle when freeing the trace tree */ |
69 | handle = tracecmd_open(filename); | 79 | tracecmd_close(handle); |
70 | if (handle) { | 80 | info->handle = handle; |
71 | trace_view_reload(info->trace_tree, handle, info->spin); | ||
72 | /* Free handle when freeing the trace tree */ | ||
73 | tracecmd_close(handle); | ||
74 | } | ||
75 | g_free(filename); | ||
76 | } | 81 | } |
77 | gtk_widget_destroy(dialog); | 82 | g_free(filename); |
83 | } | ||
84 | |||
85 | /* Callback for the clicked signal of the Load Filters button */ | ||
86 | static void | ||
87 | load_filters_clicked (gpointer data) | ||
88 | { | ||
89 | struct trace_tree_info *info = data; | ||
90 | GtkTreeView *trace_tree = GTK_TREE_VIEW(info->trace_tree); | ||
91 | struct tracecmd_xml_handle *handle; | ||
92 | gchar *filename; | ||
93 | |||
94 | filename = trace_get_file_dialog("Load Filters"); | ||
95 | if (!filename) | ||
96 | return; | ||
97 | |||
98 | handle = tracecmd_xml_open(filename); | ||
99 | if (!handle) { | ||
100 | warning("Could not open %s", filename); | ||
101 | return; | ||
102 | } | ||
103 | g_free(filename); | ||
104 | |||
105 | trace_filter_load_filters(handle, | ||
106 | "ListTaskFilter", | ||
107 | info->task_filter, | ||
108 | info->hide_tasks); | ||
109 | |||
110 | trace_view_load_filters(handle, trace_tree); | ||
111 | |||
112 | tracecmd_xml_close(handle); | ||
113 | } | ||
114 | |||
115 | /* Callback for the clicked signal of the Save Filters button */ | ||
116 | static void | ||
117 | save_filters_clicked (gpointer data) | ||
118 | { | ||
119 | struct trace_tree_info *info = data; | ||
120 | GtkTreeView *trace_tree = GTK_TREE_VIEW(info->trace_tree); | ||
121 | struct tracecmd_xml_handle *handle; | ||
122 | gchar *filename; | ||
123 | |||
124 | filename = trace_get_file_dialog("Save Filters"); | ||
125 | if (!filename) | ||
126 | return; | ||
127 | |||
128 | handle = tracecmd_xml_create(filename, VERSION_STRING); | ||
129 | if (!handle) | ||
130 | warning("Could not create %s", filename); | ||
131 | g_free(filename); | ||
132 | |||
133 | trace_filter_save_filters(handle, | ||
134 | "ListTaskFilter", | ||
135 | info->task_filter, info->hide_tasks); | ||
136 | trace_view_save_filters(handle, trace_tree); | ||
137 | |||
138 | tracecmd_xml_close(handle); | ||
78 | } | 139 | } |
79 | 140 | ||
80 | /* Callback for the clicked signal of the Exit button */ | 141 | /* Callback for the clicked signal of the Exit button */ |
@@ -162,24 +223,209 @@ cpus_clicked (gpointer data) | |||
162 | trace_view_cpu_filter_callback, trace_tree); | 223 | trace_view_cpu_filter_callback, trace_tree); |
163 | } | 224 | } |
164 | 225 | ||
165 | #if 0 | 226 | static void |
166 | static GtkTreeModel * | 227 | filter_list_clicked (gpointer data) |
167 | create_combo_box_model(void) | ||
168 | { | 228 | { |
169 | GtkListStore *store; | 229 | struct trace_tree_info *info = data; |
170 | GtkTreeIter iter; | 230 | |
231 | if (!filter_task_count(info->task_filter) && | ||
232 | !filter_task_count(info->hide_tasks)) | ||
233 | return; | ||
234 | |||
235 | info->filter_enabled ^= 1; | ||
236 | |||
237 | if (info->filter_enabled) | ||
238 | trace_view_update_filters(info->trace_tree, | ||
239 | info->task_filter, | ||
240 | info->hide_tasks); | ||
241 | else | ||
242 | trace_view_update_filters(info->trace_tree, NULL, NULL); | ||
243 | } | ||
244 | |||
245 | static void update_task_filter(struct trace_tree_info *info, | ||
246 | struct filter_task *filter) | ||
247 | { | ||
248 | struct filter_task_item *task; | ||
249 | gint pid = info->filter_task_selected; | ||
250 | |||
251 | task = filter_task_find_pid(filter, pid); | ||
252 | |||
253 | if (task) | ||
254 | filter_task_remove_pid(filter, pid); | ||
255 | else | ||
256 | filter_task_add_pid(filter, pid); | ||
257 | |||
258 | if (info->filter_enabled) | ||
259 | trace_view_update_filters(info->trace_tree, | ||
260 | info->task_filter, | ||
261 | info->hide_tasks); | ||
262 | } | ||
263 | |||
264 | static void filter_add_task_clicked(gpointer data) | ||
265 | { | ||
266 | struct trace_tree_info *info = data; | ||
267 | |||
268 | update_task_filter(info, info->task_filter); | ||
269 | } | ||
270 | |||
271 | static void filter_hide_task_clicked(gpointer data) | ||
272 | { | ||
273 | struct trace_tree_info *info = data; | ||
274 | |||
275 | update_task_filter(info, info->hide_tasks); | ||
276 | } | ||
277 | |||
278 | static void | ||
279 | filter_clear_tasks_clicked (gpointer data) | ||
280 | { | ||
281 | struct trace_tree_info *info = data; | ||
282 | |||
283 | trace_view_update_filters(info->trace_tree, NULL, NULL); | ||
284 | info->filter_enabled = 0; | ||
285 | } | ||
286 | |||
287 | static gboolean | ||
288 | do_tree_popup(GtkWidget *widget, GdkEventButton *event, gpointer data) | ||
289 | { | ||
290 | struct trace_tree_info *info = data; | ||
291 | static GtkWidget *menu; | ||
292 | static GtkWidget *menu_filter_enable; | ||
293 | static GtkWidget *menu_filter_add_task; | ||
294 | static GtkWidget *menu_filter_hide_task; | ||
295 | static GtkWidget *menu_filter_clear_tasks; | ||
296 | struct pevent *pevent; | ||
297 | struct record *record; | ||
298 | TraceViewRecord *vrec; | ||
299 | GtkTreeModel *model; | ||
300 | const char *comm; | ||
301 | gchar *text; | ||
302 | gint pid; | ||
303 | gint len; | ||
304 | guint64 offset; | ||
305 | gint row; | ||
306 | gint cpu; | ||
307 | |||
308 | if (!menu) { | ||
309 | menu = gtk_menu_new(); | ||
310 | |||
311 | menu_filter_enable = gtk_menu_item_new_with_label("Enable Filter"); | ||
312 | gtk_widget_show(menu_filter_enable); | ||
313 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_enable); | ||
314 | |||
315 | g_signal_connect_swapped (G_OBJECT (menu_filter_enable), "activate", | ||
316 | G_CALLBACK (filter_list_clicked), | ||
317 | data); | ||
318 | |||
319 | menu_filter_add_task = gtk_menu_item_new_with_label("Add Task"); | ||
320 | gtk_widget_show(menu_filter_add_task); | ||
321 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_add_task); | ||
322 | |||
323 | g_signal_connect_swapped (G_OBJECT (menu_filter_add_task), "activate", | ||
324 | G_CALLBACK (filter_add_task_clicked), | ||
325 | data); | ||
326 | |||
327 | menu_filter_hide_task = gtk_menu_item_new_with_label("Hide Task"); | ||
328 | gtk_widget_show(menu_filter_hide_task); | ||
329 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_hide_task); | ||
330 | |||
331 | g_signal_connect_swapped (G_OBJECT (menu_filter_hide_task), "activate", | ||
332 | G_CALLBACK (filter_hide_task_clicked), | ||
333 | data); | ||
334 | |||
335 | menu_filter_clear_tasks = gtk_menu_item_new_with_label("Clear Task Filter"); | ||
336 | gtk_widget_show(menu_filter_clear_tasks); | ||
337 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_clear_tasks); | ||
338 | |||
339 | g_signal_connect_swapped (G_OBJECT (menu_filter_clear_tasks), "activate", | ||
340 | G_CALLBACK (filter_clear_tasks_clicked), | ||
341 | data); | ||
342 | |||
343 | } | ||
344 | |||
345 | row = trace_view_get_selected_row(GTK_WIDGET(info->trace_tree)); | ||
346 | if (row >= 0) { | ||
347 | |||
348 | model = gtk_tree_view_get_model(GTK_TREE_VIEW(info->trace_tree)); | ||
349 | vrec = trace_view_store_get_row(TRACE_VIEW_STORE(model), row); | ||
350 | offset = vrec->offset; | ||
351 | |||
352 | record = tracecmd_read_at(info->handle, offset, &cpu); | ||
353 | |||
354 | if (record) { | ||
355 | pevent = tracecmd_get_pevent(info->handle); | ||
356 | pid = pevent_data_pid(pevent, record); | ||
357 | comm = pevent_data_comm_from_pid(pevent, pid); | ||
358 | |||
359 | len = strlen(comm) + 50; | ||
360 | |||
361 | text = g_malloc(len); | ||
362 | g_assert(text); | ||
363 | |||
364 | if (filter_task_find_pid(info->task_filter, pid)) | ||
365 | snprintf(text, len, "Remove %s-%d from filter", comm, pid); | ||
366 | else | ||
367 | snprintf(text, len, "Add %s-%d to filter", comm, pid); | ||
368 | |||
369 | info->filter_task_selected = pid; | ||
370 | |||
371 | gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_add_task), | ||
372 | text); | ||
373 | |||
374 | if (filter_task_find_pid(info->hide_tasks, pid)) | ||
375 | snprintf(text, len, "Show %s-%d", comm, pid); | ||
376 | else | ||
377 | snprintf(text, len, "Hide %s-%d", comm, pid); | ||
378 | |||
379 | gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_hide_task), | ||
380 | text); | ||
381 | |||
382 | g_free(text); | ||
383 | |||
384 | info->filter_task_selected = pid; | ||
385 | |||
386 | gtk_widget_show(menu_filter_add_task); | ||
387 | gtk_widget_show(menu_filter_hide_task); | ||
388 | free_record(record); | ||
389 | } | ||
390 | } else { | ||
391 | gtk_widget_hide(menu_filter_add_task); | ||
392 | gtk_widget_hide(menu_filter_hide_task); | ||
393 | } | ||
394 | |||
395 | if (info->filter_enabled) | ||
396 | gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_enable), | ||
397 | "Disable List Filter"); | ||
398 | else | ||
399 | gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_enable), | ||
400 | "Enable List Filter"); | ||
401 | |||
402 | if (filter_task_count(info->task_filter) || | ||
403 | filter_task_count(info->hide_tasks)) { | ||
404 | gtk_widget_set_sensitive(menu_filter_clear_tasks, TRUE); | ||
405 | gtk_widget_set_sensitive(menu_filter_enable, TRUE); | ||
406 | } else { | ||
407 | gtk_widget_set_sensitive(menu_filter_clear_tasks, FALSE); | ||
408 | gtk_widget_set_sensitive(menu_filter_enable, FALSE); | ||
409 | } | ||
410 | |||
411 | gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, | ||
412 | gtk_get_current_event_time()); | ||
413 | |||
414 | return TRUE; | ||
415 | } | ||
171 | 416 | ||
172 | store = gtk_list_store_new(1, G_TYPE_STRING); | 417 | static gboolean |
173 | gtk_list_store_append(store, &iter); | 418 | button_press_event(GtkWidget *widget, GdkEventButton *event, gpointer data) |
174 | gtk_list_store_set(store, &iter, 0, "1", -1); | 419 | { |
420 | if (event->button == 3) | ||
421 | return do_tree_popup(widget, event, data); | ||
175 | 422 | ||
176 | return GTK_TREE_MODEL(store); | 423 | return FALSE; |
177 | } | 424 | } |
178 | #endif | ||
179 | 425 | ||
180 | void trace_view(int argc, char **argv) | 426 | void trace_view(int argc, char **argv) |
181 | { | 427 | { |
182 | static struct tracecmd_input *handle; | 428 | static struct tracecmd_input *handle = NULL; |
183 | struct trace_tree_info tree_info; | 429 | struct trace_tree_info tree_info; |
184 | struct stat st; | 430 | struct stat st; |
185 | GtkWidget *trace_tree; | 431 | GtkWidget *trace_tree; |
@@ -193,6 +439,7 @@ void trace_view(int argc, char **argv) | |||
193 | GtkWidget *scrollwin; | 439 | GtkWidget *scrollwin; |
194 | GtkWidget *label; | 440 | GtkWidget *label; |
195 | GtkWidget *spin; | 441 | GtkWidget *spin; |
442 | GtkWidget *statusbar; | ||
196 | int ret; | 443 | int ret; |
197 | int c; | 444 | int c; |
198 | 445 | ||
@@ -227,10 +474,17 @@ void trace_view(int argc, char **argv) | |||
227 | if (input_file) | 474 | if (input_file) |
228 | handle = tracecmd_open(input_file); | 475 | handle = tracecmd_open(input_file); |
229 | 476 | ||
477 | memset(&tree_info, 0, sizeof(tree_info)); | ||
478 | tree_info.handle = handle; | ||
479 | tree_info.task_filter = filter_task_hash_alloc(); | ||
480 | tree_info.hide_tasks = filter_task_hash_alloc(); | ||
481 | |||
230 | /* --- Main window --- */ | 482 | /* --- Main window --- */ |
231 | 483 | ||
232 | window = gtk_window_new(GTK_WINDOW_TOPLEVEL); | 484 | window = gtk_window_new(GTK_WINDOW_TOPLEVEL); |
233 | 485 | ||
486 | trace_dialog_register_window(window); | ||
487 | |||
234 | /* --- Top Level Vbox --- */ | 488 | /* --- Top Level Vbox --- */ |
235 | 489 | ||
236 | vbox = gtk_vbox_new(FALSE, 0); | 490 | vbox = gtk_vbox_new(FALSE, 0); |
@@ -255,12 +509,11 @@ void trace_view(int argc, char **argv) | |||
255 | 509 | ||
256 | /* --- File - Load Option --- */ | 510 | /* --- File - Load Option --- */ |
257 | 511 | ||
258 | sub_item = gtk_menu_item_new_with_label("Load info"); | 512 | sub_item = gtk_menu_item_new_with_label("Load data"); |
259 | 513 | ||
260 | /* Add them to the menu */ | 514 | /* Add them to the menu */ |
261 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); | 515 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); |
262 | 516 | ||
263 | /* We can attach the Quit menu item to our exit function */ | ||
264 | g_signal_connect_swapped (G_OBJECT (sub_item), "activate", | 517 | g_signal_connect_swapped (G_OBJECT (sub_item), "activate", |
265 | G_CALLBACK (load_clicked), | 518 | G_CALLBACK (load_clicked), |
266 | (gpointer) &tree_info); | 519 | (gpointer) &tree_info); |
@@ -269,6 +522,36 @@ void trace_view(int argc, char **argv) | |||
269 | gtk_widget_show(sub_item); | 522 | gtk_widget_show(sub_item); |
270 | 523 | ||
271 | 524 | ||
525 | /* --- File - Load Filter Option --- */ | ||
526 | |||
527 | sub_item = gtk_menu_item_new_with_label("Load filters"); | ||
528 | |||
529 | /* Add them to the menu */ | ||
530 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); | ||
531 | |||
532 | g_signal_connect_swapped (G_OBJECT (sub_item), "activate", | ||
533 | G_CALLBACK (load_filters_clicked), | ||
534 | (gpointer) &tree_info); | ||
535 | |||
536 | /* We do need to show menu items */ | ||
537 | gtk_widget_show(sub_item); | ||
538 | |||
539 | |||
540 | /* --- File - Save Filter Option --- */ | ||
541 | |||
542 | sub_item = gtk_menu_item_new_with_label("Save filters"); | ||
543 | |||
544 | /* Add them to the menu */ | ||
545 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); | ||
546 | |||
547 | g_signal_connect_swapped (G_OBJECT (sub_item), "activate", | ||
548 | G_CALLBACK (save_filters_clicked), | ||
549 | (gpointer) &tree_info); | ||
550 | |||
551 | /* We do need to show menu items */ | ||
552 | gtk_widget_show(sub_item); | ||
553 | |||
554 | |||
272 | /* --- File - Quit Option --- */ | 555 | /* --- File - Quit Option --- */ |
273 | 556 | ||
274 | sub_item = gtk_menu_item_new_with_label("Quit"); | 557 | sub_item = gtk_menu_item_new_with_label("Quit"); |
@@ -385,6 +668,10 @@ void trace_view(int argc, char **argv) | |||
385 | gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); | 668 | gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); |
386 | gtk_widget_show(label); | 669 | gtk_widget_show(label); |
387 | 670 | ||
671 | gtk_signal_connect(GTK_OBJECT(trace_tree), "button_press_event", | ||
672 | (GtkSignalFunc) button_press_event, | ||
673 | (gpointer) &tree_info); | ||
674 | |||
388 | trace_view_search_setup(GTK_BOX(hbox), GTK_TREE_VIEW(trace_tree)); | 675 | trace_view_search_setup(GTK_BOX(hbox), GTK_TREE_VIEW(trace_tree)); |
389 | 676 | ||
390 | /* --- Top Level Hbox --- */ | 677 | /* --- Top Level Hbox --- */ |
@@ -407,6 +694,13 @@ void trace_view(int argc, char **argv) | |||
407 | gtk_widget_show(trace_tree); | 694 | gtk_widget_show(trace_tree); |
408 | 695 | ||
409 | 696 | ||
697 | /* --- Set up Status Bar --- */ | ||
698 | |||
699 | statusbar = trace_status_bar_new(); | ||
700 | |||
701 | gtk_box_pack_start(GTK_BOX(vbox), statusbar, FALSE, FALSE, 0); | ||
702 | gtk_widget_show(statusbar); | ||
703 | |||
410 | /********************************************** | 704 | /********************************************** |
411 | * Main Window | 705 | * Main Window |
412 | **********************************************/ | 706 | **********************************************/ |
diff --git a/trace-view-store.c b/trace-view-store.c index ba40aad..6f70ca5 100644 --- a/trace-view-store.c +++ b/trace-view-store.c | |||
@@ -1330,7 +1330,6 @@ void trace_view_store_assign_filters(TraceViewStore *store, | |||
1330 | 1330 | ||
1331 | if (store->task_filter != task_filter) | 1331 | if (store->task_filter != task_filter) |
1332 | store->task_filter = filter_task_hash_copy(task_filter); | 1332 | store->task_filter = filter_task_hash_copy(task_filter); |
1333 | |||
1334 | } | 1333 | } |
1335 | 1334 | ||
1336 | 1335 | ||
diff --git a/trace-view.c b/trace-view.c index 59e7e1a..8d121d1 100644 --- a/trace-view.c +++ b/trace-view.c | |||
@@ -870,3 +870,112 @@ void trace_view_search_setup(GtkBox *box, GtkTreeView *treeview) | |||
870 | G_CALLBACK (search_tree), | 870 | G_CALLBACK (search_tree), |
871 | (gpointer) info); | 871 | (gpointer) info); |
872 | } | 872 | } |
873 | |||
874 | int trace_view_save_filters(struct tracecmd_xml_handle *handle, | ||
875 | GtkTreeView *trace_tree) | ||
876 | { | ||
877 | struct event_filter *event_filter; | ||
878 | GtkTreeModel *model; | ||
879 | TraceViewStore *store; | ||
880 | gboolean all_events; | ||
881 | |||
882 | model = gtk_tree_view_get_model(trace_tree); | ||
883 | if (!model) | ||
884 | return -1; | ||
885 | |||
886 | store = TRACE_VIEW_STORE(model); | ||
887 | |||
888 | tracecmd_xml_start_system(handle, "TraceView"); | ||
889 | |||
890 | all_events = trace_view_store_get_all_events_enabled(store); | ||
891 | event_filter = trace_view_store_get_event_filter(store); | ||
892 | |||
893 | tracecmd_xml_start_sub_system(handle, "EventFilter"); | ||
894 | |||
895 | if (all_events || !event_filter) | ||
896 | tracecmd_xml_write_element(handle, "FilterType", "all events"); | ||
897 | else { | ||
898 | tracecmd_xml_write_element(handle, "FilterType", "filter"); | ||
899 | trace_filter_save_events(handle, event_filter); | ||
900 | } | ||
901 | |||
902 | tracecmd_xml_end_sub_system(handle); | ||
903 | |||
904 | tracecmd_xml_end_system(handle); | ||
905 | |||
906 | return 0; | ||
907 | } | ||
908 | |||
909 | static int load_event_filter(TraceViewStore *store, | ||
910 | struct tracecmd_xml_handle *handle, | ||
911 | struct tracecmd_xml_system_node *node) | ||
912 | { | ||
913 | struct tracecmd_xml_system_node *child; | ||
914 | struct event_filter *event_filter; | ||
915 | const char *name; | ||
916 | const char *value; | ||
917 | |||
918 | event_filter = trace_view_store_get_event_filter(store); | ||
919 | |||
920 | child = tracecmd_xml_node_child(node); | ||
921 | name = tracecmd_xml_node_type(child); | ||
922 | if (strcmp(name, "FilterType") != 0) | ||
923 | return -1; | ||
924 | |||
925 | value = tracecmd_xml_node_value(handle, child); | ||
926 | /* Do nothing with all events enabled */ | ||
927 | if (strcmp(value, "all events") == 0) | ||
928 | return 0; | ||
929 | |||
930 | node = tracecmd_xml_node_next(child); | ||
931 | if (!node) | ||
932 | return -1; | ||
933 | |||
934 | trace_view_store_clear_all_events_enabled(store); | ||
935 | |||
936 | trace_filter_load_events(event_filter, handle, node); | ||
937 | |||
938 | return 0; | ||
939 | } | ||
940 | |||
941 | int trace_view_load_filters(struct tracecmd_xml_handle *handle, | ||
942 | GtkTreeView *trace_tree) | ||
943 | { | ||
944 | struct tracecmd_xml_system *system; | ||
945 | struct tracecmd_xml_system_node *syschild; | ||
946 | GtkTreeModel *model; | ||
947 | TraceViewStore *store; | ||
948 | const char *name; | ||
949 | |||
950 | model = gtk_tree_view_get_model(trace_tree); | ||
951 | if (!model) | ||
952 | return -1; | ||
953 | |||
954 | store = TRACE_VIEW_STORE(model); | ||
955 | |||
956 | system = tracecmd_xml_find_system(handle, "TraceView"); | ||
957 | if (!system) | ||
958 | return -1; | ||
959 | |||
960 | syschild = tracecmd_xml_system_node(system); | ||
961 | if (!syschild) | ||
962 | goto out_free_sys; | ||
963 | |||
964 | do { | ||
965 | name = tracecmd_xml_node_type(syschild); | ||
966 | |||
967 | if (strcmp(name, "EventFilter") == 0) | ||
968 | load_event_filter(store, handle, syschild); | ||
969 | |||
970 | syschild = tracecmd_xml_node_next(syschild); | ||
971 | } while (syschild); | ||
972 | |||
973 | tracecmd_xml_free_system(system); | ||
974 | |||
975 | update_rows(trace_tree, store); | ||
976 | return 0; | ||
977 | |||
978 | out_free_sys: | ||
979 | tracecmd_xml_free_system(system); | ||
980 | return -1; | ||
981 | } | ||
diff --git a/trace-view.h b/trace-view.h index 182b285..81dac6f 100644 --- a/trace-view.h +++ b/trace-view.h | |||
@@ -23,6 +23,7 @@ | |||
23 | 23 | ||
24 | #include "trace-view-store.h" | 24 | #include "trace-view-store.h" |
25 | #include "trace-filter.h" | 25 | #include "trace-filter.h" |
26 | #include "trace-xml.h" | ||
26 | 27 | ||
27 | void | 28 | void |
28 | trace_view_load(GtkWidget *view, struct tracecmd_input *handle, | 29 | trace_view_load(GtkWidget *view, struct tracecmd_input *handle, |
@@ -65,4 +66,9 @@ void trace_view_search_setup(GtkBox *box, GtkTreeView *treeview); | |||
65 | 66 | ||
66 | gint trace_view_get_selected_row(GtkWidget *treeview); | 67 | gint trace_view_get_selected_row(GtkWidget *treeview); |
67 | 68 | ||
69 | int trace_view_save_filters(struct tracecmd_xml_handle *handle, | ||
70 | GtkTreeView *treeview); | ||
71 | int trace_view_load_filters(struct tracecmd_xml_handle *handle, | ||
72 | GtkTreeView *treeview); | ||
73 | |||
68 | #endif /* _TRACE_VIEW_H */ | 74 | #endif /* _TRACE_VIEW_H */ |
diff --git a/trace-xml.c b/trace-xml.c new file mode 100644 index 0000000..bef1f9f --- /dev/null +++ b/trace-xml.c | |||
@@ -0,0 +1,248 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> | ||
3 | * | ||
4 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU Lesser General Public | ||
7 | * License as published by the Free Software Foundation; | ||
8 | * version 2.1 of the License (not later!) | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU Lesser General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU Lesser General Public | ||
16 | * License along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | * | ||
19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
20 | */ | ||
21 | #include <stdio.h> | ||
22 | #include <stdlib.h> | ||
23 | #include <stdarg.h> | ||
24 | #include <string.h> | ||
25 | |||
26 | #include <libxml/xmlwriter.h> | ||
27 | #include <libxml/parser.h> | ||
28 | #include <libxml/xpath.h> | ||
29 | |||
30 | #include "trace-cmd.h" | ||
31 | #include "trace-xml.h" | ||
32 | |||
33 | struct tracecmd_xml_handle { | ||
34 | xmlTextWriterPtr writer; | ||
35 | xmlDocPtr doc; | ||
36 | }; | ||
37 | |||
38 | struct tracecmd_xml_system { | ||
39 | struct tracecmd_xml_handle *handle; | ||
40 | xmlXPathObjectPtr result; | ||
41 | xmlNodePtr cur; | ||
42 | }; | ||
43 | |||
44 | #define TRACE_ENCODING "UTF-8" | ||
45 | |||
46 | int tracecmd_xml_write_element(struct tracecmd_xml_handle *handle, | ||
47 | const char *obj, | ||
48 | const char *fmt, ...) | ||
49 | { | ||
50 | va_list ap; | ||
51 | int ret; | ||
52 | |||
53 | va_start(ap, fmt); | ||
54 | ret = xmlTextWriterWriteVFormatElement(handle->writer, | ||
55 | BAD_CAST obj, fmt, ap); | ||
56 | va_end(ap); | ||
57 | |||
58 | return ret; | ||
59 | } | ||
60 | |||
61 | struct tracecmd_xml_handle *tracecmd_xml_create(const char *name, | ||
62 | const char *version) | ||
63 | { | ||
64 | struct tracecmd_xml_handle *handle; | ||
65 | int ret; | ||
66 | |||
67 | handle = malloc_or_die(sizeof(*handle)); | ||
68 | memset(handle, 0, sizeof(*handle)); | ||
69 | |||
70 | handle->writer = xmlNewTextWriterFilename(name, 0); | ||
71 | if (!handle->writer) | ||
72 | goto fail_free; | ||
73 | |||
74 | ret = xmlTextWriterStartDocument(handle->writer, NULL, | ||
75 | TRACE_ENCODING, NULL); | ||
76 | if (ret < 0) | ||
77 | goto fail_close; | ||
78 | |||
79 | ret = xmlTextWriterStartElement(handle->writer, | ||
80 | BAD_CAST "KernelShark"); | ||
81 | if (ret < 0) | ||
82 | goto fail_close; | ||
83 | |||
84 | return handle; | ||
85 | |||
86 | fail_close: | ||
87 | xmlFreeTextWriter(handle->writer); | ||
88 | fail_free: | ||
89 | free(handle); | ||
90 | return NULL; | ||
91 | } | ||
92 | |||
93 | int tracecmd_xml_start_system(struct tracecmd_xml_handle *handle, | ||
94 | const char *system) | ||
95 | { | ||
96 | int ret; | ||
97 | |||
98 | ret = xmlTextWriterStartElement(handle->writer, | ||
99 | BAD_CAST system); | ||
100 | |||
101 | if (ret < 0) | ||
102 | return ret; | ||
103 | |||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | int tracecmd_xml_start_sub_system(struct tracecmd_xml_handle *handle, | ||
108 | const char *subsystem) | ||
109 | { | ||
110 | int ret; | ||
111 | |||
112 | ret = xmlTextWriterStartElement(handle->writer, | ||
113 | BAD_CAST subsystem); | ||
114 | |||
115 | return ret; | ||
116 | } | ||
117 | |||
118 | void tracecmd_xml_end_system(struct tracecmd_xml_handle *handle) | ||
119 | { | ||
120 | xmlTextWriterEndElement(handle->writer); | ||
121 | } | ||
122 | |||
123 | void tracecmd_xml_end_sub_system(struct tracecmd_xml_handle *handle) | ||
124 | { | ||
125 | xmlTextWriterEndElement(handle->writer); | ||
126 | } | ||
127 | |||
128 | void tracecmd_xml_close(struct tracecmd_xml_handle *handle) | ||
129 | { | ||
130 | if (handle->writer) { | ||
131 | xmlTextWriterEndElement(handle->writer); | ||
132 | xmlTextWriterEndDocument(handle->writer); | ||
133 | xmlFreeTextWriter(handle->writer); | ||
134 | } | ||
135 | if (handle->doc) { | ||
136 | xmlFreeDoc(handle->doc); | ||
137 | } | ||
138 | free(handle); | ||
139 | } | ||
140 | |||
141 | /***********************************************************/ | ||
142 | /*** Reading XML files ***/ | ||
143 | /***********************************************************/ | ||
144 | |||
145 | |||
146 | struct tracecmd_xml_handle *tracecmd_xml_open(const char *file) | ||
147 | { | ||
148 | struct tracecmd_xml_handle *handle; | ||
149 | |||
150 | handle = malloc_or_die(sizeof(*handle)); | ||
151 | memset(handle, 0, sizeof(*handle)); | ||
152 | |||
153 | handle->doc = xmlParseFile(file); | ||
154 | if (!handle->doc) | ||
155 | goto fail_free; | ||
156 | |||
157 | return handle; | ||
158 | |||
159 | fail_free: | ||
160 | free(handle); | ||
161 | return NULL; | ||
162 | } | ||
163 | |||
164 | struct tracecmd_xml_system * | ||
165 | tracecmd_xml_find_system(struct tracecmd_xml_handle *handle, | ||
166 | const char *system) | ||
167 | { | ||
168 | struct tracecmd_xml_system *sys; | ||
169 | xmlXPathContextPtr context; | ||
170 | xmlXPathObjectPtr result; | ||
171 | xmlChar *xpath; | ||
172 | char *path; | ||
173 | |||
174 | path = malloc_or_die(strlen(system) + 3); | ||
175 | sprintf(path, "//%s", system); | ||
176 | xpath = BAD_CAST path; | ||
177 | |||
178 | context = xmlXPathNewContext(handle->doc); | ||
179 | result = xmlXPathEvalExpression(xpath, context); | ||
180 | free(path); | ||
181 | |||
182 | if (xmlXPathNodeSetIsEmpty(result->nodesetval)) { | ||
183 | xmlXPathFreeObject(result); | ||
184 | return NULL; | ||
185 | } | ||
186 | |||
187 | sys = malloc_or_die(sizeof(*sys)); | ||
188 | sys->handle = handle; | ||
189 | sys->result = result; | ||
190 | sys->cur = result->nodesetval->nodeTab[0]->xmlChildrenNode; | ||
191 | |||
192 | return sys; | ||
193 | } | ||
194 | |||
195 | struct tracecmd_xml_system_node * | ||
196 | tracecmd_xml_system_node(struct tracecmd_xml_system *system) | ||
197 | { | ||
198 | return (struct tracecmd_xml_system_node *)system->cur; | ||
199 | } | ||
200 | |||
201 | const char *tracecmd_xml_node_type(struct tracecmd_xml_system_node *tnode) | ||
202 | { | ||
203 | xmlNodePtr node = (xmlNodePtr)tnode; | ||
204 | return (const char *)node->name; | ||
205 | } | ||
206 | |||
207 | struct tracecmd_xml_system_node * | ||
208 | tracecmd_xml_node_child(struct tracecmd_xml_system_node *tnode) | ||
209 | { | ||
210 | xmlNodePtr node = (xmlNodePtr)tnode; | ||
211 | return (struct tracecmd_xml_system_node *)node->xmlChildrenNode; | ||
212 | } | ||
213 | |||
214 | struct tracecmd_xml_system_node * | ||
215 | tracecmd_xml_node_next(struct tracecmd_xml_system_node *tnode) | ||
216 | { | ||
217 | xmlNodePtr node = (xmlNodePtr)tnode; | ||
218 | return (struct tracecmd_xml_system_node *)node->next; | ||
219 | } | ||
220 | |||
221 | const char *tracecmd_xml_node_value(struct tracecmd_xml_handle *handle, | ||
222 | struct tracecmd_xml_system_node *tnode) | ||
223 | { | ||
224 | xmlNodePtr node = (xmlNodePtr)tnode; | ||
225 | return (const char *)xmlNodeListGetString(handle->doc, node->xmlChildrenNode, 1); | ||
226 | } | ||
227 | |||
228 | void tracecmd_xml_free_system(struct tracecmd_xml_system *system) | ||
229 | { | ||
230 | xmlXPathFreeObject(system->result); | ||
231 | free(system); | ||
232 | } | ||
233 | |||
234 | int tracecmd_xml_system_exists(struct tracecmd_xml_handle *handle, | ||
235 | const char *system) | ||
236 | { | ||
237 | struct tracecmd_xml_system *sys; | ||
238 | int exists = 0; | ||
239 | |||
240 | sys = tracecmd_xml_find_system(handle, system); | ||
241 | if (sys) { | ||
242 | exists = 1; | ||
243 | tracecmd_xml_free_system(sys); | ||
244 | } | ||
245 | |||
246 | return exists; | ||
247 | } | ||
248 | |||
diff --git a/trace-xml.h b/trace-xml.h new file mode 100644 index 0000000..d1f62b0 --- /dev/null +++ b/trace-xml.h | |||
@@ -0,0 +1,62 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> | ||
3 | * | ||
4 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU Lesser General Public | ||
7 | * License as published by the Free Software Foundation; | ||
8 | * version 2.1 of the License (not later!) | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU Lesser General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU Lesser General Public | ||
16 | * License along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | * | ||
19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
20 | */ | ||
21 | #ifndef __TRACE_XML_H | ||
22 | #define __TRACE_XML_H | ||
23 | |||
24 | struct tracecmd_xml_handle; | ||
25 | struct tacecmd_xml_system; | ||
26 | struct tacecmd_xml_system_node; | ||
27 | |||
28 | struct tracecmd_xml_handle *tracecmd_xml_create(const char *name, const char *version); | ||
29 | struct tracecmd_xml_handle *tracecmd_xml_open(const char *name); | ||
30 | void tracecmd_xml_close(struct tracecmd_xml_handle *handle); | ||
31 | |||
32 | int tracecmd_xml_start_system(struct tracecmd_xml_handle *handle, | ||
33 | const char *system); | ||
34 | void tracecmd_xml_end_system(struct tracecmd_xml_handle *handle); | ||
35 | |||
36 | int tracecmd_xml_start_sub_system(struct tracecmd_xml_handle *handle, | ||
37 | const char *subsystem); | ||
38 | void tracecmd_xml_end_sub_system(struct tracecmd_xml_handle *handle); | ||
39 | |||
40 | int tracecmd_xml_write_element(struct tracecmd_xml_handle *handle, | ||
41 | const char *obj, | ||
42 | const char *fmt, ...); | ||
43 | |||
44 | struct tracecmd_xml_handle *tracecmd_xml_open(const char *file); | ||
45 | |||
46 | struct tracecmd_xml_system * | ||
47 | tracecmd_xml_find_system(struct tracecmd_xml_handle *handle, | ||
48 | const char *system); | ||
49 | void tracecmd_xml_free_system(struct tracecmd_xml_system *system); | ||
50 | struct tracecmd_xml_system_node * | ||
51 | tracecmd_xml_system_node(struct tracecmd_xml_system *system); | ||
52 | const char *tracecmd_xml_node_type(struct tracecmd_xml_system_node *tnode); | ||
53 | struct tracecmd_xml_system_node * | ||
54 | tracecmd_xml_node_child(struct tracecmd_xml_system_node *tnode); | ||
55 | struct tracecmd_xml_system_node * | ||
56 | tracecmd_xml_node_next(struct tracecmd_xml_system_node *tnode); | ||
57 | const char *tracecmd_xml_node_value(struct tracecmd_xml_handle *handle, | ||
58 | struct tracecmd_xml_system_node *tnode); | ||
59 | int tracecmd_xml_system_exists(struct tracecmd_xml_handle *handle, | ||
60 | const char *system); | ||
61 | |||
62 | #endif /* __TRACE_XML_H */ | ||