1 /* 2 * Copyright (c) 2021 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 package ohos.devtools.views.trace.fragment; 17 18 import ohos.devtools.views.trace.Sql; 19 import ohos.devtools.views.trace.bean.FunctionBean; 20 import ohos.devtools.views.trace.bean.ThreadData; 21 import ohos.devtools.views.trace.component.AnalystPanel; 22 import ohos.devtools.views.trace.component.ContentPanel; 23 import ohos.devtools.views.trace.util.Db; 24 import ohos.devtools.views.trace.util.Utils; 25 import org.jetbrains.annotations.NotNull; 26 27 import javax.swing.JComponent; 28 import javax.swing.SwingUtilities; 29 import java.awt.Graphics2D; 30 import java.awt.event.KeyEvent; 31 import java.awt.event.MouseEvent; 32 import java.awt.geom.Rectangle2D; 33 import java.util.ArrayList; 34 import java.util.List; 35 import java.util.Objects; 36 import java.util.concurrent.CompletableFuture; 37 38 /** 39 * Thread data row 40 * 41 * @since 2021/04/22 12:25 42 */ 43 public class ThreadDataFragment extends AbstractDataFragment<ThreadData> implements ThreadData.IEventListener { 44 /** 45 * graph event callback 46 */ 47 protected static ThreadData currentSelectedThreadData; 48 49 /** 50 * Thread object 51 */ 52 public ThreadData thread; 53 54 /** 55 * delayClickStartTime 56 */ 57 public Long delayClickStartTime; 58 59 /** 60 * delayClickProcessName 61 */ 62 private String delayClickProcessName; 63 64 private int x1; 65 private int x2; 66 private Rectangle2D bounds; 67 private boolean isLoading; 68 private boolean funcLoad = false; 69 70 /** 71 * structure 72 * 73 * @param root root 74 * @param thread thread 75 */ ThreadDataFragment(JComponent root, ThreadData thread)76 public ThreadDataFragment(JComponent root, ThreadData thread) { 77 super(root, true, false); 78 this.thread = thread; 79 this.setRoot(root); 80 } 81 82 /** 83 * getData 84 * 85 * @return List<ThreadData> 86 */ getData()87 public List<ThreadData> getData() { 88 return this.data; 89 } 90 91 /** 92 * Drawing method 93 * 94 * @param graphics graphics 95 */ 96 @Override draw(Graphics2D graphics)97 public void draw(Graphics2D graphics) { 98 super.draw(graphics); 99 100 // Supplement the information on the left 101 graphics.setColor(getRoot().getForeground()); 102 String name = thread.getThreadName() + " " + thread.getTid(); 103 bounds = graphics.getFontMetrics().getStringBounds(name, graphics); 104 double wordWidth = bounds.getWidth() / name.length(); // Width per character 105 double wordNum = (getDescRect().getWidth() - 40) / wordWidth; 106 if (bounds.getWidth() < getDescRect().getWidth() - 40) { // Direct line display 107 graphics.drawString(name, (int) (getDescRect().getX() + 10), 108 (int) (getDescRect().getY() + bounds.getHeight() + 10)); 109 } else { 110 String substring = name.substring((int) wordNum); 111 if (substring.length() < wordNum) { 112 graphics.drawString(name.substring(0, (int) wordNum), (int) (getDescRect().getX() + 10), 113 (int) (getDescRect().getY() + bounds.getHeight() + 8)); 114 graphics.drawString(substring, Utils.getX(getDescRect()) + 10, 115 (int) (getDescRect().getY() + bounds.getHeight() * 2 + 8)); 116 } else { 117 graphics.drawString(name.substring(0, (int) wordNum), (int) (getDescRect().getX() + 10), 118 (int) (getDescRect().getY() + bounds.getHeight() + 2)); 119 graphics.drawString(substring.substring(0, (int) wordNum), (int) (getDescRect().getX() + 10), 120 (int) (getDescRect().getY() + bounds.getHeight() * 2 + 2)); 121 graphics.drawString(substring.substring((int) wordNum), (int) (getDescRect().getX() + 10), 122 (int) (getDescRect().getY() + bounds.getHeight() * 3 + 2)); 123 } 124 } 125 drawData(graphics); 126 } 127 drawData(Graphics2D graphics)128 private void drawData(Graphics2D graphics) { 129 if (data != null) { 130 data.stream().filter(threadData -> threadData.getStartTime() + threadData.getDuration() > startNS 131 && threadData.getStartTime() < endNS).forEach(threadData -> { 132 if (threadData.getStartTime() < startNS) { 133 x1 = getX(startNS); 134 } else { 135 x1 = getX(threadData.getStartTime()); 136 } 137 if (threadData.getStartTime() + threadData.getDuration() > endNS) { 138 x2 = getX(endNS); 139 } else { 140 x2 = getX(threadData.getStartTime() + threadData.getDuration()); 141 } 142 threadData 143 .setRect(x1 + Utils.getX(getDataRect()), Utils.getY(getDataRect()) + 5, x2 - x1 <= 0 ? 1 : x2 - x1, 144 getDataRect().height - 10); 145 threadData.root = getRoot(); 146 threadData.setEventListener(this); 147 threadData.draw(graphics); 148 }); 149 } else { 150 graphics.setColor(getRoot().getForeground()); 151 graphics.drawString("Loading...", Utils.getX(getDataRect()), Utils.getY(getDataRect()) + 12); 152 loadData(); 153 } 154 } 155 156 /** 157 * Mouse clicked event 158 * 159 * @param event event 160 */ 161 @Override mouseClicked(MouseEvent event)162 public void mouseClicked(MouseEvent event) { 163 super.mouseClicked(event); 164 if (data != null) { 165 data.stream().filter(threadData -> threadData.getStartTime() + threadData.getDuration() > startNS 166 && threadData.getStartTime() < endNS).filter(threadData -> threadData.edgeInspect(event)).findFirst() 167 .ifPresent(threadData -> { 168 threadData.setProcessName(thread.getProcessName()); 169 threadData.setThreadName(thread.getThreadName()); 170 threadData.onClick(event); 171 }); 172 } 173 } 174 175 /** 176 * Mouse pressed event 177 * 178 * @param event event 179 */ 180 @Override mousePressed(MouseEvent event)181 public void mousePressed(MouseEvent event) { 182 } 183 184 /** 185 * Mouse exited event 186 * 187 * @param event event 188 */ 189 @Override mouseExited(MouseEvent event)190 public void mouseExited(MouseEvent event) { 191 } 192 193 /** 194 * Mouse entered event 195 * 196 * @param event event 197 */ 198 @Override mouseEntered(MouseEvent event)199 public void mouseEntered(MouseEvent event) { 200 } 201 202 /** 203 * Mouse moved event 204 * 205 * @param evt event 206 */ 207 @Override mouseMoved(MouseEvent evt)208 public void mouseMoved(MouseEvent evt) { 209 MouseEvent event = getRealMouseEvent(evt); 210 super.mouseMoved(event); 211 clearFocus(event); 212 if (edgeInspect(event)) { 213 if (data != null) { 214 data.stream().filter(threadData -> threadData.getStartTime() + threadData.getDuration() > startNS 215 && threadData.getStartTime() < endNS).filter(threadData -> threadData.edgeInspect(event)) 216 .findFirst().ifPresent(filter -> { 217 filter.onMouseMove(event); 218 if (filter.edgeInspect(event)) { 219 if (!filter.flagFocus) { 220 filter.flagFocus = true; 221 filter.onFocus(event); 222 } 223 } else { 224 if (filter.flagFocus) { 225 filter.flagFocus = false; 226 filter.onBlur(event); 227 } 228 } 229 }); 230 } 231 } 232 } 233 234 /** 235 * Mouse released event 236 * 237 * @param event event 238 */ 239 @Override mouseReleased(MouseEvent event)240 public void mouseReleased(MouseEvent event) { 241 } 242 243 /** 244 * key released event 245 * 246 * @param event event 247 */ 248 @Override keyReleased(KeyEvent event)249 public void keyReleased(KeyEvent event) { 250 } 251 loadData()252 private void loadData() { 253 if (!isLoading) { 254 isLoading = true; 255 CompletableFuture.runAsync(() -> { 256 List<ThreadData> list = new ArrayList<>() { 257 }; 258 Db.getInstance().query(st -> addStatement(st), Sql.SYS_QUERY_THREAD_DATA, list, thread.getTid()); 259 data = list; 260 ArrayList<FunctionBean> functionBeans = new ArrayList<>() { 261 }; 262 Db.getInstance() 263 .query(st -> addStatement(st), Sql.SYS_GET_FUN_DATA_BY_TID, functionBeans, thread.getTid()); 264 SwingUtilities.invokeLater(() -> { 265 isLoading = false; 266 if (!functionBeans.isEmpty()) { 267 if (!funcLoad) { 268 funcLoad = true; 269 int maxHeight = 270 (functionBeans.stream().mapToInt(bean -> bean.getDepth()).max().getAsInt() + 1) * 20; 271 FunctionDataFragment functionDataFragment = 272 getFunctionDataFragment(functionBeans, maxHeight); 273 if (this.getRoot() instanceof ContentPanel) { 274 ContentPanel contentPanel = (ContentPanel) this.getRoot(); 275 int index = contentPanel.fragmentList.indexOf(this) + 1; 276 contentPanel.addDataFragment(index, functionDataFragment); 277 contentPanel.refresh(); 278 } 279 } else { 280 repaint(); 281 } 282 } else { 283 repaint(); 284 } 285 if (delayClickStartTime != null) { 286 data.stream().filter(it -> it.getStartTime() == delayClickStartTime).findFirst() 287 .ifPresent(it -> { 288 it.setProcessName(delayClickProcessName); 289 click(null, it); 290 delayClickStartTime = null; 291 delayClickProcessName = null; 292 }); 293 } 294 }); 295 }, Utils.getPool()).whenComplete((unused, throwable) -> { 296 if (Objects.nonNull(throwable)) { 297 throwable.printStackTrace(); 298 } 299 }); 300 } 301 } 302 303 @NotNull getFunctionDataFragment(ArrayList<FunctionBean> functionBeans, int maxHeight)304 private FunctionDataFragment getFunctionDataFragment(ArrayList<FunctionBean> functionBeans, int maxHeight) { 305 FunctionDataFragment functionDataFragment = 306 new FunctionDataFragment(this.getRoot(), functionBeans); 307 functionDataFragment.parentUuid = this.parentUuid; 308 functionDataFragment.thread = this.thread; 309 functionDataFragment.defaultHeight = maxHeight + 20; 310 functionDataFragment.visible = true; 311 return functionDataFragment; 312 } 313 314 /** 315 * click event 316 * 317 * @param event event 318 * @param data data 319 */ 320 @Override click(MouseEvent event, ThreadData data)321 public void click(MouseEvent event, ThreadData data) { 322 clearSelected(); 323 data.select(true); 324 data.repaint(); 325 currentSelectedThreadData = data; 326 if (AnalystPanel.iThreadDataClick != null) { 327 AnalystPanel.iThreadDataClick.click(data); 328 } 329 } 330 331 /** 332 * Mouse blur event 333 * 334 * @param event event 335 * @param data data 336 */ 337 @Override blur(MouseEvent event, ThreadData data)338 public void blur(MouseEvent event, ThreadData data) { 339 } 340 341 /** 342 * Mouse focus event 343 * 344 * @param event event 345 * @param data data 346 */ 347 @Override focus(MouseEvent event, ThreadData data)348 public void focus(MouseEvent event, ThreadData data) { 349 } 350 351 /** 352 * Mouse move event 353 * 354 * @param event event 355 * @param data data 356 */ 357 @Override mouseMove(MouseEvent event, ThreadData data)358 public void mouseMove(MouseEvent event, ThreadData data) { 359 getRoot().repaint(); 360 } 361 getDelayClickProcessName()362 public String getDelayClickProcessName() { 363 return delayClickProcessName; 364 } 365 setDelayClickProcessName(String delayClickProcessName)366 public void setDelayClickProcessName(String delayClickProcessName) { 367 this.delayClickProcessName = delayClickProcessName; 368 } 369 } 370