1#!/usr/bin/env python3 2# -*- coding: UTF-8 -*- 3 4# Copyright (c) 2023 Huawei Device Co., Ltd. 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17import os, shutil, stat 18import tkinter 19import tkinter.filedialog 20from tkinter import * 21from tkinter import ttk 22from tkinter.messagebox import showinfo 23from ffrt_trace_process import * 24 25class TraceParserUI(Tk): 26 def __init__(self): 27 # User Interface Window Define 28 self.window = tkinter.Tk() 29 self.window.title('FFRT Trace分析辅助工具') 30 self.window.geometry('1320x640') 31 self.window.resizable(0, 0) 32 33 # Trace Filepath 34 self.trace_file = None 35 # Trace Logs 36 self.logs = None 37 self.logs_supplement = None 38 # Process 39 self.pid = None 40 self.curr_pid = None 41 self.pid_list = [] 42 self.ffrt_found = False 43 self.active_process_map = None 44 self.switch_log_map = None 45 self.D = None 46 # Thread 47 self.tid = None 48 self.tid_list = [] 49 self.tname_list = [] 50 # Task 51 self.task_name = None 52 self.task_name_list = [] 53 self.task_infos = None 54 55 # Start Main Activity 56 self.init_window() 57 self.window.mainloop() 58 59 60 def reset_content(self): 61 self.D = None 62 self.tid_list = [] 63 self.tname_list = [] 64 65 self.ffrt_tid_check['value'] = ('--查看线程信息--') 66 self.ffrt_tid_check.current(0) 67 self.ffrt_taskname_check['value'] = ('--查看task信息--') 68 self.ffrt_taskname_check.current(0) 69 self.text_output.delete(0.0, tkinter.END) 70 self.statistics_output.delete(0.0, tkinter.END) 71 72 return 73 74 75 def choose_trace_file(self): 76 filename = tkinter.filedialog.askopenfilename() 77 78 if not os.path.isfile(filename): 79 showinfo( 80 title='Warning', 81 message="文件不存在:" + filename 82 ) 83 return 84 85 self.ffrt_pid_check['value'] = ('--下拉选择进程--') 86 self.ffrt_pid_check.current(0) 87 self.curr_pid = None 88 self.reset_content() 89 self.text_output.insert(tkinter.INSERT, "正在解析文件:" + filename.split('/')[-1] + "\n") 90 self.text_output.update() 91 92 # open trace file 93 self.trace_file = filename 94 self.logs = open(self.trace_file, 'r', encoding="gb18030", errors="ignore").readlines() 95 clean_logs(self.logs) 96 97 # find ffrt processes 98 ffrt_process, self.active_process_map, self.switch_log_map = extract_active_pid_and_switch_log(self.logs) 99 if len(ffrt_process) > 0: 100 self.pid_list = ffrt_process 101 self.ffrt_found = True 102 self.text_output.insert(tkinter.INSERT, "解析完成:共检测到%d个进程,其中%d个FFRT相关进程:\n\n\n" % 103 (len(self.active_process_map.keys()), len(self.pid_list))) 104 else: 105 self.pid_list = list(self.active_process_map.keys()) 106 self.ffrt_found = False 107 self.text_output.insert(tkinter.INSERT, "解析完成:共检测到%d个进程,未发现FFRT相关进程:\n\n\n" % 108 len(self.active_process_map.keys())) 109 110 pid_list_shown = copy.deepcopy(self.pid_list) 111 pid_list_shown.insert(0, self.ffrt_pid_check['value'][0]) 112 self.ffrt_pid_check['value'] = pid_list_shown 113 114 return 115 116 117 def on_select_pid(self, event): 118 if self.pid.get() == '--下拉选择进程--': 119 return 120 121 pid = int(self.pid.get()) 122 if self.curr_pid != pid: 123 self.curr_pid = pid 124 self.reset_content() 125 126 self.D, self.logs_supplement = process_trace(self.logs, pid, self.active_process_map, self.switch_log_map) 127 self.tid_list = list(self.active_process_map[pid].keys()) 128 self.tname_list = list(self.active_process_map[pid].values()) 129 self.text_output.insert(tkinter.INSERT, "检测到进程%d内共%d个线程\n" % (pid, len(self.tname_list))) 130 tnames_shown = list(self.tname_list) 131 tnames_shown.insert(0, self.ffrt_tid_check['value'][0]) 132 self.ffrt_tid_check['value'] = tnames_shown 133 134 task_names = list(self.D["task"]["infos"].keys()) 135 self.text_output.insert(tkinter.INSERT, "检测进程%d内共%d种task类型\n" % (pid, len(task_names))) 136 task_names.insert(0, self.ffrt_taskname_check['value'][0]) 137 self.ffrt_taskname_check['value'] = task_names 138 139 self.statistics_output.delete(0.0, tkinter.END) 140 lines = print_summary(self.D) 141 for line in lines: 142 self.statistics_output.insert(tkinter.INSERT, line) 143 144 return 145 146 147 def on_select_tid(self, event): 148 if self.tid.get() == '--查看线程信息--': 149 self.statistics_output.delete(0.0, tkinter.END) 150 return 151 152 tname = self.tid.get() 153 154 self.statistics_output.delete(0.0, tkinter.END) 155 type = None 156 if tname in self.D["thread"]["worker"]["S"].keys(): 157 type = "worker" 158 else: 159 type = "non-worker" 160 lines = print_hist(self.D["thread"][type]["S"][tname]["statistics"]) 161 for line in lines: 162 self.statistics_output.insert(tkinter.INSERT, line) 163 lines = print_switch(self.D["thread"][type]["S"][tname]["statistics"]["switch_out"]) 164 for line in lines: 165 self.statistics_output.insert(tkinter.INSERT, line) 166 self.statistics_scroll.config(command=self.statistics_output.yview) 167 168 return 169 170 171 def on_select_task_name(self, event): 172 if self.task_name.get() == '--查看task信息--': 173 self.statistics_output.delete(0.0, tkinter.END) 174 return 175 176 task_name = str(self.task_name.get()) 177 178 self.statistics_output.delete(0.0, tkinter.END) 179 lines = print_task_info(task_name, self.D["task"]["infos"][task_name]) 180 for line in lines: 181 self.statistics_output.insert(tkinter.INSERT, line) 182 183 return 184 185 186 def save_result(self): 187 if self.ffrt_found is True: 188 out_dir = self.trace_file + "_result" 189 if not os.path.exists(out_dir): 190 os.mkdir(out_dir) 191 else: 192 shutil.rmtree(out_dir) 193 os.mkdir(out_dir) 194 195 for pid in self.pid_list: 196 self.D, _ = process_trace(self.logs, pid, self.active_process_map, self.switch_log_map) 197 write_infos(os.path.join(out_dir, str(pid)), None, self.D) 198 199 logs_supplement = self.logs 200 for pid in self.pid_list: 201 _, _, _, _, logs_supplement = parse_and_convert_task_trace(logs_supplement, pid) 202 203 with os.fdopen( 204 os.open(out_dir + "/trace_refine.ftrace", os.O_WRONLY | os.O_CREAT | os.O_EXCL, 205 stat.S_IWUSR | stat.S_IRUSR), 206 'w') as file: 207 file.writelines(logs_supplement) 208 file.close() 209 210 self.text_output.insert(tkinter.INSERT, "解析结果保存至: %s\n" % out_dir) 211 else: 212 self.text_output.insert(tkinter.INSERT, "未检测到FFRT进程,无效操作\n") 213 214 return 215 216 217 def init_window(self): 218 # Frame00 219 self.frame00 = Frame(self.window, width=1280, height=200) 220 self.frame00.columnconfigure(0, weight=1) 221 self.frame00.rowconfigure(0, weight=1) 222 223 # Frame001 224 self.frame001 = Frame(self.frame00, width=50, height=200) 225 226 self.btn_choose_trace_file = Button(self.frame001, text="选择trace文件", bg="lightgreen", width=20, height=2, command=self.choose_trace_file) 227 self.btn_choose_trace_file.grid(column=0, row=0, pady=10, padx=0) 228 229 self.btn_save_csv = Button(self.frame001, text="保存解析结果", bg="lightblue", width=20, height=2, command=self.save_result) 230 self.btn_save_csv.grid(column=0, row=1, pady=20, padx=0) 231 232 self.frame001.grid(column=0, row=0, pady=0, padx=0) 233 234 # Frame002 235 self.frame002 = Frame(self.frame00, width=50, height=200) 236 237 self.pid = tkinter.StringVar() 238 self.ffrt_pid_check = ttk.Combobox(self.frame002, textvariable=self.pid) 239 self.ffrt_pid_check.grid(column=0, row=0, pady=0, padx=0) 240 self.ffrt_pid_check['value'] = ('--下拉选择进程--') 241 self.ffrt_pid_check['state'] = 'readonly' 242 self.ffrt_pid_check.bind('<<ComboboxSelected>>', self.on_select_pid) 243 self.ffrt_pid_check.current(0) 244 245 self.frame002.grid(column=1, row=0, pady=0, padx=50) 246 247 # Frame003 248 self.frame003 = Frame(self.frame00, width=200, height=200) 249 250 self.tid = tkinter.StringVar() 251 self.ffrt_tid_check = ttk.Combobox(self.frame003, textvariable=self.tid) 252 self.ffrt_tid_check.grid(column=0, row=1, pady=25, padx=0) 253 self.ffrt_tid_check['value'] = ('--查看线程信息--') 254 self.ffrt_tid_check['state'] = 'readonly' 255 self.ffrt_tid_check.bind('<<ComboboxSelected>>', self.on_select_tid) 256 self.ffrt_tid_check.current(0) 257 258 self.task_name = tkinter.StringVar() 259 self.ffrt_taskname_check = ttk.Combobox(self.frame003, textvariable=self.task_name) 260 self.ffrt_taskname_check.grid(column=0, row=2, pady=25, padx=0) 261 self.ffrt_taskname_check['value'] = ('--查看task信息--') 262 self.ffrt_taskname_check['state'] = 'readonly' 263 self.ffrt_taskname_check.bind('<<ComboboxSelected>>', self.on_select_task_name) 264 self.ffrt_taskname_check.current(0) 265 266 self.frame003.grid(column=2, row=0, pady=0, padx=50) 267 268 # Frame004 269 self.frame004 = Frame(self.frame00, width=70, height=200) 270 271 self.text_output = Text(self.frame004, height=10, width=70) 272 self.text_output.grid(column=0, row=0, pady=0, padx=0) 273 274 self.frame004.grid(column=3, row=0, pady=0, padx=0) 275 276 self.frame00.grid(column=0, row=0, columnspan=3, padx=10) 277 self.frame00.grid_propagate(0) 278 279 # Frame10 280 self.frame10 = Frame(self.window, bd=5, width=1280, height=330, relief="groove") 281 self.frame10.columnconfigure(0, weight=1) 282 self.frame10.grid(column=0, row=1, columnspan=3, padx=20) 283 self.frame10.grid_propagate(0) 284 285 self.statistics_scroll = ttk.Scrollbar(self.frame10) 286 self.statistics_scroll.grid(column=1, row=0, sticky=NS) 287 self.statistics_output = Text(self.frame10, yscrollcommand=self.statistics_scroll.set) 288 self.statistics_output.grid(column=0, row=0, sticky=NSEW) 289 290if __name__ == '__main__': 291 TraceParserUI()