Stefan Schuermans commited on 2020-05-24 11:27:30
Showing 2 changed files, with 117 additions and 56 deletions.
... | ... |
@@ -182,6 +182,7 @@ |
182 | 182 |
<property name="search_column">0</property> |
183 | 183 |
<property name="fixed_height_mode">True</property> |
184 | 184 |
<property name="enable_tree_lines">True</property> |
185 |
+ <signal name="row-activated" handler="onDetailsRowActivated" swapped="no"/> |
|
185 | 186 |
<child internal-child="selection"> |
186 | 187 |
<object class="GtkTreeSelection"/> |
187 | 188 |
</child> |
... | ... |
@@ -122,6 +122,7 @@ class UptGui: |
122 | 122 |
self.widProcessesView = self.builder.get_object('ProcessesView') |
123 | 123 |
handlers = { |
124 | 124 |
'onDestroy': self.onDestroy, |
125 |
+ 'onDetailsRowActivated': self.onDetailsRowActivated, |
|
125 | 126 |
'onProcessesCursorChanged': self.onProcessesCursorChanged |
126 | 127 |
} |
127 | 128 |
self.builder.connect_signals(handlers) |
... | ... |
@@ -134,30 +135,131 @@ class UptGui: |
134 | 135 |
""" |
135 | 136 |
Gtk.main_quit() |
136 | 137 |
|
138 |
+ def onDetailsRowActivated(self, widget, row, col): |
|
139 |
+ """ |
|
140 |
+ Row in details view has been activated. |
|
141 |
+ """ |
|
142 |
+ # get proc_id of selected row (if any) |
|
143 |
+ detail_sel = self.widDetailsView.get_selection() |
|
144 |
+ if detail_sel is None: |
|
145 |
+ return |
|
146 |
+ detail_iter = detail_sel.get_selected()[1] |
|
147 |
+ if detail_iter is None: |
|
148 |
+ return |
|
149 |
+ proc_id = self.widDetailsTree.get_value(detail_iter, |
|
150 |
+ self.DETAIL_PROC_ID) |
|
151 |
+ # do nothing for rows without valid proc_id |
|
152 |
+ if proc_id < 0: |
|
153 |
+ return |
|
154 |
+ # select process |
|
155 |
+ self.selectProcess(proc_id) |
|
156 |
+ # show details of selected process |
|
157 |
+ self.showDetails(proc_id) |
|
158 |
+ |
|
137 | 159 |
def onProcessesCursorChanged(self, widget): |
138 | 160 |
""" |
139 | 161 |
Cursor changed in processes tree view. |
140 | 162 |
""" |
141 | 163 |
# get proc_id of selected process |
142 |
- proc_id = None |
|
143 | 164 |
proc_sel = self.widProcessesView.get_selection() |
144 |
- if proc_sel is not None: |
|
165 |
+ if proc_sel is None: |
|
166 |
+ self.showDetails(None) |
|
167 |
+ return |
|
145 | 168 |
proc_iter = proc_sel.get_selected()[1] |
146 |
- if proc_iter is not None: |
|
147 |
- proc_id = self.widProcessesTree.get_value( |
|
148 |
- proc_iter, self.PROC_PROC_ID) |
|
169 |
+ if proc_iter is None: |
|
170 |
+ self.showDetails(None) |
|
171 |
+ return |
|
172 |
+ proc_id = self.widProcessesTree.get_value(proc_iter, self.PROC_PROC_ID) |
|
173 |
+ # show details of selected process |
|
174 |
+ self.showDetails(proc_id) |
|
175 |
+ |
|
176 |
+ def openTrace(self, proto_filename: str): |
|
177 |
+ """ |
|
178 |
+ Open a trace file. |
|
179 |
+ """ |
|
180 |
+ # forget old processes |
|
181 |
+ self.widProcessesTree.clear() |
|
182 |
+ # lead new data |
|
183 |
+ with open(proto_filename, 'rb') as proto_file: |
|
184 |
+ self.processes = uproctrace.processes.Processes(proto_file) |
|
185 |
+ # add processes to processes tree store |
|
186 |
+ to_be_output = [(self.processes.toplevel, None)] |
|
187 |
+ while to_be_output: |
|
188 |
+ procs, parent_iter = to_be_output[-1] |
|
189 |
+ if not procs: |
|
190 |
+ del to_be_output[-1] |
|
191 |
+ continue |
|
192 |
+ proc = procs[0] |
|
193 |
+ del procs[0] |
|
194 |
+ proc_iter = self.widProcessesTree.append(parent_iter) |
|
195 |
+ self.widProcessesTree.set_value(proc_iter, self.PROC_PROC_ID, |
|
196 |
+ proc.proc_id) |
|
197 |
+ self.widProcessesTree.set_value(proc_iter, |
|
198 |
+ self.PROC_BEGIN_TIMESTAMP, |
|
199 |
+ proc.begin_timestamp) |
|
200 |
+ self.widProcessesTree.set_value( |
|
201 |
+ proc_iter, self.PROC_BEGIN_TIMESTAMP_TEXT, |
|
202 |
+ timestamp2str(proc.begin_timestamp)) |
|
203 |
+ self.widProcessesTree.set_value(proc_iter, self.PROC_END_TIMESTAMP, |
|
204 |
+ proc.end_timestamp) |
|
205 |
+ self.widProcessesTree.set_value(proc_iter, |
|
206 |
+ self.PROC_END_TIMESTAMP_TEXT, |
|
207 |
+ timestamp2str(proc.end_timestamp)) |
|
208 |
+ self.widProcessesTree.set_value(proc_iter, self.PROC_CMDLINE, |
|
209 |
+ cmdline2str(proc.cmdline)) |
|
210 |
+ self.widProcessesTree.set_value(proc_iter, self.PROC_CPU_TIME, |
|
211 |
+ proc.cpu_time) |
|
212 |
+ self.widProcessesTree.set_value(proc_iter, self.PROC_CPU_TIME_TEXT, |
|
213 |
+ duration2str(proc.cpu_time)) |
|
214 |
+ self.widProcessesTree.set_value(proc_iter, self.PROC_MAX_RSS_KB, |
|
215 |
+ proc.max_rss_kb) |
|
216 |
+ self.widProcessesTree.set_value(proc_iter, |
|
217 |
+ self.PROC_MAX_RSS_KB_TEXT, |
|
218 |
+ kb2str(proc.max_rss_kb)) |
|
219 |
+ to_be_output.append((proc.children, proc_iter)) |
|
220 |
+ # show all processes |
|
221 |
+ self.widProcessesView.expand_all() |
|
222 |
+ |
|
223 |
+ def selectProcess(self, proc_id: int): |
|
224 |
+ """ |
|
225 |
+ Select a process. |
|
226 |
+ """ |
|
227 |
+ # get selection |
|
228 |
+ proc_sel = self.widProcessesView.get_selection() |
|
229 |
+ if proc_sel is None: |
|
230 |
+ return |
|
231 |
+ # deselect all processes |
|
232 |
+ proc_sel.unselect_all() |
|
233 |
+ # leave if invalid proc_id |
|
234 |
+ if proc_id is None or proc_id < 0: |
|
235 |
+ return |
|
236 |
+ # select process with proc_id |
|
237 |
+ # scroll the process into view |
|
238 |
+ def update(proc_store, proc_path, proc_iter, _ctx): |
|
239 |
+ if proc_store.get_value(proc_iter, self.PROC_PROC_ID) != proc_id: |
|
240 |
+ return |
|
241 |
+ proc_sel.select_iter(proc_iter) |
|
242 |
+ self.widProcessesView.scroll_to_cell(proc_path) |
|
243 |
+ |
|
244 |
+ self.widProcessesTree.foreach(update, None) |
|
245 |
+ |
|
246 |
+ def showDetails(self, proc_id: int): |
|
247 |
+ """ |
|
248 |
+ Show details of process. |
|
249 |
+ """ |
|
149 | 250 |
# forget old details |
150 | 251 |
self.widDetailsTree.clear() |
151 |
- # leave if no process selected |
|
152 |
- if proc_id is None: |
|
252 |
+ # leave if invalid proc_id |
|
253 |
+ # get process |
|
254 |
+ if proc_id is None or proc_id < 0: |
|
153 | 255 |
return |
154 |
- # get process, leave if not found |
|
155 | 256 |
proc = self.processes.getProcess(proc_id) |
156 | 257 |
if proc is None: |
157 | 258 |
return |
158 | 259 |
# add details of new process |
159 | 260 |
def add(key: str, value: str, parent_iter=None): |
160 | 261 |
detail_iter = self.widDetailsTree.append(parent_iter) |
262 |
+ self.widDetailsTree.set_value(detail_iter, self.DETAIL_PROC_ID, -1) |
|
161 | 263 |
self.widDetailsTree.set_value(detail_iter, self.DETAIL_KEY, key) |
162 | 264 |
self.widDetailsTree.set_value(detail_iter, self.DETAIL_VALUE, |
163 | 265 |
value) |
... | ... |
@@ -169,9 +271,12 @@ class UptGui: |
169 | 271 |
list_iter = add(key, f'{len(values):d} entries', parent_iter) |
170 | 272 |
for i, value in enumerate(values): |
171 | 273 |
add(f'{key} {i:d}', value, list_iter) |
274 |
+ return list_iter |
|
172 | 275 |
|
173 | 276 |
add('begin time', timestamp2str(proc.begin_timestamp)) |
174 |
- add_list('command line', proc.cmdline) |
|
277 |
+ cmdline_iter = add_list('command line', proc.cmdline) |
|
278 |
+ self.widDetailsView.expand_row( |
|
279 |
+ self.widDetailsTree.get_path(cmdline_iter), True) |
|
175 | 280 |
add('CPU time', duration2str(proc.cpu_time)) |
176 | 281 |
add('end time', timestamp2str(proc.end_timestamp)) |
177 | 282 |
add_list('environment', sorted(proc.environ)) |
... | ... |
@@ -199,53 +304,8 @@ class UptGui: |
199 | 304 |
cmdline2str(child_proc.cmdline), list_iter) |
200 | 305 |
self.widDetailsTree.set_value(child_iter, self.DETAIL_PROC_ID, |
201 | 306 |
child_proc.proc_id) |
202 |
- |
|
203 |
- def openTrace(self, proto_filename: str): |
|
204 |
- """ |
|
205 |
- Open a trace file. |
|
206 |
- """ |
|
207 |
- # forget old processes |
|
208 |
- self.widProcessesTree.clear() |
|
209 |
- # lead new data |
|
210 |
- with open(proto_filename, 'rb') as proto_file: |
|
211 |
- self.processes = uproctrace.processes.Processes(proto_file) |
|
212 |
- # add processes to processes tree store |
|
213 |
- to_be_output = [(self.processes.toplevel, None)] |
|
214 |
- while to_be_output: |
|
215 |
- procs, parent_iter = to_be_output[-1] |
|
216 |
- if not procs: |
|
217 |
- del to_be_output[-1] |
|
218 |
- continue |
|
219 |
- proc = procs[0] |
|
220 |
- del procs[0] |
|
221 |
- proc_iter = self.widProcessesTree.append(parent_iter) |
|
222 |
- self.widProcessesTree.set_value(proc_iter, self.PROC_PROC_ID, |
|
223 |
- proc.proc_id) |
|
224 |
- self.widProcessesTree.set_value(proc_iter, |
|
225 |
- self.PROC_BEGIN_TIMESTAMP, |
|
226 |
- proc.begin_timestamp) |
|
227 |
- self.widProcessesTree.set_value( |
|
228 |
- proc_iter, self.PROC_BEGIN_TIMESTAMP_TEXT, |
|
229 |
- timestamp2str(proc.begin_timestamp)) |
|
230 |
- self.widProcessesTree.set_value(proc_iter, self.PROC_END_TIMESTAMP, |
|
231 |
- proc.end_timestamp) |
|
232 |
- self.widProcessesTree.set_value(proc_iter, |
|
233 |
- self.PROC_END_TIMESTAMP_TEXT, |
|
234 |
- timestamp2str(proc.end_timestamp)) |
|
235 |
- self.widProcessesTree.set_value(proc_iter, self.PROC_CMDLINE, |
|
236 |
- cmdline2str(proc.cmdline)) |
|
237 |
- self.widProcessesTree.set_value(proc_iter, self.PROC_CPU_TIME, |
|
238 |
- proc.cpu_time) |
|
239 |
- self.widProcessesTree.set_value(proc_iter, self.PROC_CPU_TIME_TEXT, |
|
240 |
- duration2str(proc.cpu_time)) |
|
241 |
- self.widProcessesTree.set_value(proc_iter, self.PROC_MAX_RSS_KB, |
|
242 |
- proc.max_rss_kb) |
|
243 |
- self.widProcessesTree.set_value(proc_iter, |
|
244 |
- self.PROC_MAX_RSS_KB_TEXT, |
|
245 |
- kb2str(proc.max_rss_kb)) |
|
246 |
- to_be_output.append((proc.children, proc_iter)) |
|
247 |
- # show all processes |
|
248 |
- self.widProcessesView.expand_all() |
|
307 |
+ self.widDetailsView.expand_row( |
|
308 |
+ self.widDetailsTree.get_path(list_iter), True) |
|
249 | 309 |
|
250 | 310 |
|
251 | 311 |
def main(argv): |
252 | 312 |