68a2a133e36677b1ec10e421e8ba9e83f81b2e45
Stefan Schuermans begin of upt-gui: processes...

Stefan Schuermans authored 4 years ago

1) #! /usr/bin/env python3
2) 
3) import gi
4) import os
5) import shlex
6) import sys
7) import time
8) gi.require_version('Gtk', '3.0')
9) from gi.repository import Gtk
10) 
11) import uproctrace.processes
12) 
13) 
14) def cmdline2str(cmdline: list) -> str:
15)     """
16)     Convert command line to string.
17)     """
18)     if cmdline is None:
Stefan Schuermans upt-gui: implement details...

Stefan Schuermans authored 4 years ago

19)         return '???'
Stefan Schuermans begin of upt-gui: processes...

Stefan Schuermans authored 4 years ago

20)     return ' '.join([shlex.quote(s) for s in cmdline])
21) 
Stefan Schuermans upt-gui: implement details...

Stefan Schuermans authored 4 years ago

22) 
23) def duration2str(duration: float) -> str:
24)     """
25)     Convert duration to string.
26)     """
27)     if duration is None:
28)         return '???'
29)     # split into day, hours, minutes, seconds
30)     s = int(duration)
31)     m = s // 60
32)     s = s % 60
33)     h = m // 60
34)     m = m % 60
35)     d = h // 24
36)     h = h % 24
37)     # split into ms, us, ns
38)     ns = int((duration - s) * 1e9)
39)     us = ns // 1000
40)     ns = ns % 1000
41)     ms = us // 1000
42)     us = us % 1000
43)     # assemble text
44)     txt = ''
45)     if d > 0:
46)         txt += f'{d:d} d '
47)     if h > 0 or txt:
48)         txt += f'{h:d} h '
49)     if m > 0 or txt:
50)         txt += f'{m:d} m '
51)     if s > 0 or txt:
52)         txt += f'{s:d} s '
53)     if ms > 0 or txt:
54)         txt += f'{ms:d} ms '
55)     if us > 0 or txt:
56)         txt += f'{us:d} us '
57)     txt += f'{ns:d} ns '
58)     txt += f'({duration:f} s)'
59)     return txt
60) 
61) 
62) def kb2str(kb: int) -> str:
63)     """
64)     Convert size in KiB to string.
65)     """
66)     if kb is None:
67)         return '???'
68)     # split into GiB, MiB, KiB
69)     mib = kb // 1024
70)     kib = kb % 1024
71)     gib = mib // 1024
72)     mib = mib % 1024
73)     # assemble text
74)     txt = ''
75)     if gib > 0:
76)         txt += f'{gib:d} GiB '
77)     if mib > 0 or txt:
78)         txt += f'{mib:d} MiB '
79)     txt += f'{kib:d} KiB '
80)     txt += f'({kb:d} KiB)'
81)     return txt
82) 
83) 
Stefan Schuermans begin of upt-gui: processes...

Stefan Schuermans authored 4 years ago

84) def timestamp2str(timestamp: float) -> str:
85)     """
86)     Convert a timestamp to a human-reable time string."
87)     """
88)     if timestamp is None:
Stefan Schuermans upt-gui: implement details...

Stefan Schuermans authored 4 years ago

89)         return '???'
Stefan Schuermans begin of upt-gui: processes...

Stefan Schuermans authored 4 years ago

90)     sec = int(timestamp)
91)     nsec = int((timestamp - sec) * 1e9)
92)     time_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(sec))
93)     return time_str + f'.{nsec:09d}'
94) 
95) 
96) class UptGui:
Stefan Schuermans upt-gui: implement details...

Stefan Schuermans authored 4 years ago

97) 
Stefan Schuermans upt-gui: show parent/children

Stefan Schuermans authored 4 years ago

98)     DETAIL_PROC_ID = 0
99)     DETAIL_KEY = 1
100)     DETAIL_VALUE = 2
Stefan Schuermans upt-gui: implement details...

Stefan Schuermans authored 4 years ago

101)     PROC_PROC_ID = 0
Stefan Schuermans upt-gui: use value (not str...

Stefan Schuermans authored 4 years ago

102)     PROC_BEGIN_TIMESTAMP = 1
103)     PROC_BEGIN_TIMESTAMP_TEXT = 2
104)     PROC_END_TIMESTAMP = 3
105)     PROC_END_TIMESTAMP_TEXT = 4
106)     PROC_CMDLINE = 5
107)     PROC_CPU_TIME = 6
108)     PROC_CPU_TIME_TEXT = 7
109)     PROC_MAX_RSS_KB = 8
110)     PROC_MAX_RSS_KB_TEXT = 9
Stefan Schuermans upt-gui: implement details...

Stefan Schuermans authored 4 years ago

111) 
Stefan Schuermans begin of upt-gui: processes...

Stefan Schuermans authored 4 years ago

112)     def __init__(self, proto_filename):
113)         """
114)         Construct the GUI.
115)         """
116)         self.builder = Gtk.Builder()
117)         script_dir = os.path.dirname(os.path.abspath(__file__))
118)         self.builder.add_from_file(os.path.join(script_dir, 'upt-gui.glade'))
Stefan Schuermans upt-gui: implement details...

Stefan Schuermans authored 4 years ago

119)         self.widDetailsTree = self.builder.get_object('DetailsTree')
120)         self.widDetailsView = self.builder.get_object('DetailsView')
Stefan Schuermans begin of upt-gui: processes...

Stefan Schuermans authored 4 years ago

121)         self.widProcessesTree = self.builder.get_object('ProcessesTree')
122)         self.widProcessesView = self.builder.get_object('ProcessesView')
Stefan Schuermans upt-gui: implement details...

Stefan Schuermans authored 4 years ago

123)         handlers = {
124)             'onDestroy': self.onDestroy,
Stefan Schuermans upt-gui: implement double-c...

Stefan Schuermans authored 4 years ago

125)             'onDetailsRowActivated': self.onDetailsRowActivated,
Stefan Schuermans upt-gui: implement details...

Stefan Schuermans authored 4 years ago

126)             'onProcessesCursorChanged': self.onProcessesCursorChanged
127)         }
Stefan Schuermans begin of upt-gui: processes...

Stefan Schuermans authored 4 years ago

128)         self.builder.connect_signals(handlers)
129)         # open trace file
130)         self.openTrace(proto_filename)
131) 
132)     def onDestroy(self, widget):
133)         """
134)         Window will be destroyed.
135)         """
136)         Gtk.main_quit()
137) 
Stefan Schuermans upt-gui: implement double-c...

Stefan Schuermans authored 4 years ago

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) 
Stefan Schuermans upt-gui: implement details...

Stefan Schuermans authored 4 years ago

159)     def onProcessesCursorChanged(self, widget):
160)         """
161)         Cursor changed in processes tree view.
162)         """
163)         # get proc_id of selected process
164)         proc_sel = self.widProcessesView.get_selection()
Stefan Schuermans upt-gui: implement double-c...

Stefan Schuermans authored 4 years ago

165)         if proc_sel is None:
166)             self.showDetails(None)
Stefan Schuermans upt-gui: implement details...

Stefan Schuermans authored 4 years ago

167)             return
Stefan Schuermans upt-gui: implement double-c...

Stefan Schuermans authored 4 years ago

168)         proc_iter = proc_sel.get_selected()[1]
169)         if proc_iter is None:
170)             self.showDetails(None)
Stefan Schuermans upt-gui: implement details...

Stefan Schuermans authored 4 years ago

171)             return
Stefan Schuermans upt-gui: implement double-c...

Stefan Schuermans authored 4 years ago

172)         proc_id = self.widProcessesTree.get_value(proc_iter, self.PROC_PROC_ID)
173)         # show details of selected process
174)         self.showDetails(proc_id)
Stefan Schuermans upt-gui: implement details...

Stefan Schuermans authored 4 years ago

175) 
Stefan Schuermans begin of upt-gui: processes...

Stefan Schuermans authored 4 years ago

176)     def openTrace(self, proto_filename: str):
177)         """
178)         Open a trace file.
179)         """
Stefan Schuermans upt-gui: implement details...

Stefan Schuermans authored 4 years ago

180)         # forget old processes
Stefan Schuermans begin of upt-gui: processes...

Stefan Schuermans authored 4 years ago

181)         self.widProcessesTree.clear()
182)         # lead new data
183)         with open(proto_filename, 'rb') as proto_file:
Stefan Schuermans upt-gui: implement details...

Stefan Schuermans authored 4 years ago

184)             self.processes = uproctrace.processes.Processes(proto_file)
Stefan Schuermans begin of upt-gui: processes...

Stefan Schuermans authored 4 years ago

185)         # add processes to processes tree store
Stefan Schuermans upt-gui: implement details...

Stefan Schuermans authored 4 years ago

186)         to_be_output = [(self.processes.toplevel, None)]
Stefan Schuermans begin of upt-gui: processes...

Stefan Schuermans authored 4 years ago

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)
Stefan Schuermans upt-gui: implement details...

Stefan Schuermans authored 4 years ago

195)             self.widProcessesTree.set_value(proc_iter, self.PROC_PROC_ID,
196)                                             proc.proc_id)
Stefan Schuermans upt-gui: use value (not str...

Stefan Schuermans authored 4 years ago

197)             self.widProcessesTree.set_value(proc_iter,
198)                                             self.PROC_BEGIN_TIMESTAMP,
199)                                             proc.begin_timestamp)
Stefan Schuermans upt-gui: implement details...

Stefan Schuermans authored 4 years ago

200)             self.widProcessesTree.set_value(
Stefan Schuermans upt-gui: use value (not str...

Stefan Schuermans authored 4 years ago

201)                 proc_iter, self.PROC_BEGIN_TIMESTAMP_TEXT,
Stefan Schuermans upt-gui: implement details...

Stefan Schuermans authored 4 years ago

202)                 timestamp2str(proc.begin_timestamp))
Stefan Schuermans upt-gui: use value (not str...

Stefan Schuermans authored 4 years ago

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,
Stefan Schuermans upt-gui: implement details...

Stefan Schuermans authored 4 years ago

207)                                             timestamp2str(proc.end_timestamp))
Stefan Schuermans show cpu time and memory in...

Stefan Schuermans authored 4 years ago

208)             self.widProcessesTree.set_value(proc_iter, self.PROC_CMDLINE,
Stefan Schuermans upt-gui: implement details...

Stefan Schuermans authored 4 years ago

209)                                             cmdline2str(proc.cmdline))
Stefan Schuermans show cpu time and memory in...

Stefan Schuermans authored 4 years ago

210)             self.widProcessesTree.set_value(proc_iter, self.PROC_CPU_TIME,
Stefan Schuermans upt-gui: use value (not str...

Stefan Schuermans authored 4 years ago

211)                                             proc.cpu_time)
212)             self.widProcessesTree.set_value(proc_iter, self.PROC_CPU_TIME_TEXT,
Stefan Schuermans show cpu time and memory in...

Stefan Schuermans authored 4 years ago

213)                                             duration2str(proc.cpu_time))
214)             self.widProcessesTree.set_value(proc_iter, self.PROC_MAX_RSS_KB,
Stefan Schuermans upt-gui: use value (not str...

Stefan Schuermans authored 4 years ago

215)                                             proc.max_rss_kb)
216)             self.widProcessesTree.set_value(proc_iter,
217)                                             self.PROC_MAX_RSS_KB_TEXT,
Stefan Schuermans show cpu time and memory in...

Stefan Schuermans authored 4 years ago

218)                                             kb2str(proc.max_rss_kb))
Stefan Schuermans begin of upt-gui: processes...

Stefan Schuermans authored 4 years ago

219)             to_be_output.append((proc.children, proc_iter))
220)         # show all processes
221)         self.widProcessesView.expand_all()
222) 
Stefan Schuermans upt-gui: implement double-c...

Stefan Schuermans authored 4 years ago

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)         """
250)         # forget old details
251)         self.widDetailsTree.clear()
252)         # leave if invalid proc_id
253)         # get process
254)         if proc_id is None or proc_id < 0:
255)             return
256)         proc = self.processes.getProcess(proc_id)
257)         if proc is None:
258)             return
259)         # add details of new process
260)         def add(key: str, value: str, parent_iter=None):
261)             detail_iter = self.widDetailsTree.append(parent_iter)
262)             self.widDetailsTree.set_value(detail_iter, self.DETAIL_PROC_ID, -1)
263)             self.widDetailsTree.set_value(detail_iter, self.DETAIL_KEY, key)
264)             self.widDetailsTree.set_value(detail_iter, self.DETAIL_VALUE,
265)                                           value)
266)             return detail_iter
267) 
268)         def add_list(key: str, values: list, parent_iter=None):
269)             if values is None:
270)                 return add(key, '???', parent_iter)
271)             list_iter = add(key, f'{len(values):d} entries', parent_iter)
272)             for i, value in enumerate(values):
273)                 add(f'{key} {i:d}', value, list_iter)
274)             return list_iter
275) 
276)         add('begin time', timestamp2str(proc.begin_timestamp))
277)         cmdline_iter = add_list('command line', proc.cmdline)
278)         self.widDetailsView.expand_row(
279)             self.widDetailsTree.get_path(cmdline_iter), True)
280)         add('CPU time', duration2str(proc.cpu_time))
281)         add('end time', timestamp2str(proc.end_timestamp))
282)         add_list('environment', sorted(proc.environ))
283)         add('executable', proc.exe)
284)         add('max. resident memory', kb2str(proc.max_rss_kb))
285)         add('system CPU time', duration2str(proc.sys_time))
286)         add('user CPU time', duration2str(proc.user_time))
287)         add('working directory', proc.cwd)
288)         # add parent
289)         parent_proc = proc.parent
290)         if parent_proc is None:
291)             add('parent', '???')
292)         else:
293)             parent_iter = add('parent', cmdline2str(parent_proc.cmdline))
294)             self.widDetailsTree.set_value(parent_iter, self.DETAIL_PROC_ID,
295)                                           parent_proc.proc_id)
296)         # add children
297)         child_procs = proc.children
298)         if child_procs is None:
299)             add('children', '???')
300)         else:
301)             list_iter = add('children', f'{len(child_procs):d} entries')
302)             for i, child_proc in enumerate(child_procs):
303)                 child_iter = add(f'child {i:d}',
304)                                  cmdline2str(child_proc.cmdline), list_iter)
305)                 self.widDetailsTree.set_value(child_iter, self.DETAIL_PROC_ID,
306)                                               child_proc.proc_id)
307)             self.widDetailsView.expand_row(
308)                 self.widDetailsTree.get_path(list_iter), True)
309)