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 |