• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.distributed.component;
17 
18 import com.intellij.ui.components.JBLabel;
19 import com.intellij.ui.components.JBPanel;
20 import com.intellij.ui.components.JBScrollPane;
21 import net.miginfocom.swing.MigLayout;
22 import ohos.devtools.views.applicationtrace.util.TimeUtils;
23 import ohos.devtools.views.distributed.DistributedDataPane;
24 import ohos.devtools.views.distributed.bean.DistributedFuncBean;
25 import ohos.devtools.views.distributed.util.DistributedCache;
26 import ohos.devtools.views.trace.AbstractRow;
27 import ohos.devtools.views.trace.EventDispatcher;
28 import ohos.devtools.views.trace.ExpandPanel;
29 import ohos.devtools.views.trace.Tip;
30 import ohos.devtools.views.trace.TraceSimpleRow;
31 import ohos.devtools.views.trace.util.Utils;
32 
33 import javax.swing.SwingUtilities;
34 import java.awt.Component;
35 import java.awt.Container;
36 import java.awt.Point;
37 import java.awt.Rectangle;
38 import java.awt.event.ComponentAdapter;
39 import java.awt.event.ComponentEvent;
40 import java.awt.event.MouseAdapter;
41 import java.awt.event.MouseEvent;
42 import java.awt.event.MouseMotionAdapter;
43 import java.util.ArrayList;
44 import java.util.Arrays;
45 import java.util.List;
46 import java.util.Locale;
47 import java.util.Objects;
48 import java.util.Optional;
49 import java.util.concurrent.atomic.AtomicBoolean;
50 import java.util.stream.Collectors;
51 
52 /**
53  * TracePanel
54  *
55  * @since 2021/5/13 13:06
56  */
57 public class DistributedTracePanel extends JBPanel {
58     /**
59      * currentSelectThreadIds
60      */
61     public static final List<Integer> CURRENT_SELECT_THREAD_IDS = new ArrayList<>();
62 
63     /**
64      * root TracePanel
65      */
66     public static DistributedTracePanel root;
67 
68     /**
69      * current start time
70      */
71     public static long startNS;
72 
73     /**
74      * current end time
75      */
76     public static long endNS;
77 
78     /**
79      * DURATION 10_000_000_000L
80      */
81     private static long DURATION = 0L;
82 
83     /**
84      * range start time
85      */
86     private static Long rangeStartNS;
87 
88     /**
89      * range end time
90      */
91     private static Long rangeEndNS;
92 
93     private boolean isDragRelease = false;
94     private DistributedTimeShaft timeShaft;
95     private JBScrollPane scrollPane;
96     private JBPanel contentPanel;
97     private List<Component> componentList;
98     private Point startPoint;
99     private Point endPoint;
100     private List<Component> allComponent;
101     private boolean isInit = false;
102     private JBLabel totalLabel = new JBLabel();
103     private JBLabel rangeStartLabel = new JBLabel();
104 
105     /**
106      * structure function
107      */
DistributedTracePanel()108     public DistributedTracePanel() {
109         timeShaft = new DistributedTimeShaft((startNS, endNS, scale) -> {
110             EventDispatcher.dispatcherRange(startNS, endNS, scale);
111             Arrays.stream(contentPanel.getComponents()).filter(DeviceExpandPanel.class::isInstance)
112                 .map(it -> ((DeviceExpandPanel) it)).forEach(it -> it.refresh(startNS, endNS));
113             rangeStartLabel.setText(ohos.devtools.views.trace.util.TimeUtils.getSecondFromNSecond(startNS));
114         }, keyEvent -> timeShaftComplete(), mouseEvent -> timeShaftComplete());
115         contentPanel = new JBPanel();
116         contentPanel.setLayout(new MigLayout("inset 0,wrap 1", "0[grow,fill]0", "0[]0"));
117         contentPanel.setFocusable(true);
118         contentPanel.setBorder(null);
119         setLayout(new MigLayout("inset 0", "0[115!]0[grow,fill]0", "0[]0"));
120         scrollPane = new JBScrollPane(contentPanel);
121         scrollPane.setBorder(null);
122         totalLabel.setText("Total: " + TimeUtils.getDistributedTotalTime(DistributedTracePanel.getDURATION()));
123         add(totalLabel, "span 1 1,align center");
124         add(new DistributedRuler(DistributedTracePanel.getDURATION()), "pushx,growx, h 20!,wrap");
125         add(rangeStartLabel, "span 1 1,align right,gapright 5");
126         add(timeShaft, "pushx,growx,h 30!,wrap");
127         add(scrollPane, "span 2,push,grow");
128         setBorder(null);
129         scrollPane.getVerticalScrollBar().addAdjustmentListener(event -> Arrays.stream(contentPanel.getComponents())
130             .filter(it -> scrollPane.getViewport().getViewRect().intersects(it.getBounds()))
131             .filter(DeviceExpandPanel.class::isInstance).map(it -> ((DeviceExpandPanel) it))
132             .forEach(it -> it.refresh(startNS, endNS)));
133         root = this;
134         contentPanel.addMouseListener(new ContentMouseAdapter());
135         contentPanel.addMouseMotionListener(new ContentMouseMotionAdapter());
136         contentPanel.addComponentListener(new ContentComponentAdapter());
137         // Add monitor to change the selected func color block according to funcId
138         addMonitorChange();
139     }
140 
addMonitorChange()141     private void addMonitorChange() {
142         EventDispatcher.addFuncSelectChange(funcId -> {
143             if (Objects.nonNull(DistributedFuncBean.currentSelectedFunc)) {
144                 DistributedFuncBean.currentSelectedFunc.setSelected(false);
145             }
146             allComponent.stream().filter(TraceFuncRow.class::isInstance)
147                 .map(it -> ((TraceFuncRow<DistributedFuncBean>) it)).forEach(row -> {
148                 if (row.getData() != null) {
149                     row.getData().stream().filter(it -> it.getId() == funcId).forEach(it -> {
150                         it.setSelected(true);
151                         // Rectangle is the bounds after row expansion
152                         DistributedFuncBean.currentSelectedFunc = it;
153                         int offsetHeight = 0;
154                         for (Component component : scrollPane.getRootPane().getLayeredPane().getComponents()) {
155                             if (component.getClass() == DistributedDataPane.class) {
156                                 offsetHeight = component.getBounds().height;
157                             }
158                         }
159                         int finalOffsetHeight = offsetHeight;
160                         if (row.isCollapsed()) {
161                             row.setCollapsed(false, rectangle1 -> {
162                                 Rectangle rct =
163                                     SwingUtilities.convertRectangle(row.content, it.getRect(), contentPanel);
164                                 scrollPane.getVerticalScrollBar().setValue(0);
165                                 scrollPane.getViewport().scrollRectToVisible(new Rectangle(0,
166                                     Utils.getY(rct) + finalOffsetHeight + it.getRect().y + it.getRect().height,
167                                     row.getBounds().width, rct.height));
168                                 long rangeX1 = it.getStartTs() - it.getDur() * 2;
169                                 if (rangeX1 < 0) {
170                                     rangeX1 = 0;
171                                 }
172                                 long rangeX2 = it.getEndTs() + it.getDur() * 2;
173                                 timeShaft.setRange(rangeX1, rangeX2);
174                                 timeShaft.notifyRangeChange(rangeX1, rangeX2);
175                             });
176                         } else {
177                             rowUnCollapsed(row, it, finalOffsetHeight);
178                         }
179                     });
180                 }
181             });
182         });
183     }
184 
rowUnCollapsed(TraceFuncRow<DistributedFuncBean> row, DistributedFuncBean it, int finalOffsetHeight)185     private void rowUnCollapsed(TraceFuncRow<DistributedFuncBean> row, DistributedFuncBean it,
186         int finalOffsetHeight) {
187         Rectangle rct = SwingUtilities.convertRectangle(row.content, it.getRect(), contentPanel);
188         scrollPane.getVerticalScrollBar().setValue(0);
189         scrollPane.getViewport().scrollRectToVisible(
190             new Rectangle(0, Utils.getY(rct) + finalOffsetHeight + it.getRect().y + it.getRect().height,
191                 row.getBounds().width, rct.height));
192         long rangeX1 = it.getStartTs() - it.getDur() * 2;
193         if (rangeX1 < 0) {
194             rangeX1 = 0;
195         }
196         long rangeX2 = it.getEndTs() + it.getDur() * 2;
197         timeShaft.setRange(rangeX1, rangeX2);
198         timeShaft.notifyRangeChange(rangeX1, rangeX2);
199     }
200 
201     /**
202      * range end time
203      *
204      * @return get current contentPanel
205      */
getContentPanel()206     public JBPanel getContentPanel() {
207         return contentPanel;
208     }
209 
timeShaftComplete()210     private void timeShaftComplete() {
211         Arrays.stream(contentPanel.getComponents()).filter(it -> it instanceof ExpandPanel)
212             .map(it -> ((ExpandPanel) it)).filter(it -> !it.isCollapsed()).forEach(
213             it -> Arrays.stream(it.getContent().getComponents()).filter(row -> row instanceof TraceSimpleRow)
214                 .map(row -> ((TraceSimpleRow) row))
215                 .filter(row -> row.getRowName().toLowerCase(Locale.ENGLISH).startsWith("cpu"))
216                 .forEach(row -> row.reload()));
217     }
218 
tip(MouseEvent event)219     private void tip(MouseEvent event) {
220         if (Objects.isNull(allComponent)) {
221             allComponent = Arrays.stream(contentPanel.getComponents()).filter(DeviceExpandPanel.class::isInstance)
222                 .map(it -> ((DeviceExpandPanel) it)).flatMap(it -> Arrays.stream(it.getContent().getComponents()))
223                 .filter(ExpandPanel.class::isInstance).map(it -> ((ExpandPanel) it))
224                 .flatMap(it -> Arrays.stream(it.getContent().getComponents())).collect(Collectors.toList());
225         }
226         boolean flag = allComponent.stream().anyMatch(it -> {
227             if (it instanceof AbstractRow) {
228                 AbstractRow row = (AbstractRow) it;
229                 Rectangle rectangle = SwingUtilities.convertRectangle(row, row.getContentBounds(), contentPanel);
230                 return rectangle.contains(event.getPoint());
231             }
232             return false;
233         });
234         if (flag) {
235             allComponent.forEach(component -> {
236                 if (component instanceof AbstractRow) {
237                     AbstractRow row = (AbstractRow) component;
238                     Optional<ExpandPanel> exp = getExpandPanel(row);
239                     Optional<DeviceExpandPanel> dxp = getDevicePanel(row);
240                     if (exp.isPresent() && dxp.isPresent() && !exp.get().isCollapsed() && !dxp.get().isCollapsed()) {
241                         Rectangle rectangle =
242                             SwingUtilities.convertRectangle(row, row.getContentBounds(), contentPanel);
243                         if (rectangle.contains(event.getPoint())) {
244                             Point point = SwingUtilities.convertPoint(contentPanel, event.getPoint(), row.content);
245                             row.mouseMoveHandler(point);
246                         }
247                     }
248                 }
249             });
250         } else {
251             Tip.getInstance().hidden();
252         }
253     }
254 
255     /**
256      * structure function
257      *
258      * @param startNS startNS
259      * @param endNS endNS
260      */
setRange(long startNS, long endNS)261     public void setRange(long startNS, long endNS) {
262         Optional.ofNullable(timeShaft).ifPresent(tf -> tf.setRange(startNS, endNS));
263     }
264 
notifySelectRangeChange()265     private void notifySelectRangeChange() {
266         if (Objects.isNull(DistributedTracePanel.rangeStartNS) && Objects.isNull(DistributedTracePanel.rangeEndNS)) {
267             EventDispatcher.dispatcherThreadRange(DistributedTracePanel.startNS, DistributedTracePanel.endNS,
268                 CURRENT_SELECT_THREAD_IDS);
269         } else {
270             long st =
271                 DistributedTracePanel.rangeStartNS < DistributedTracePanel.startNS ? DistributedTracePanel.startNS
272                     : DistributedTracePanel.rangeStartNS;
273             long et = DistributedTracePanel.rangeEndNS > DistributedTracePanel.endNS ? DistributedTracePanel.endNS
274                 : DistributedTracePanel.rangeEndNS;
275             EventDispatcher.dispatcherThreadRange(st, et, CURRENT_SELECT_THREAD_IDS);
276         }
277     }
278 
getTimeShaft()279     public DistributedTimeShaft getTimeShaft() {
280         return timeShaft;
281     }
282 
getDevicePanel(Container component)283     private Optional<DeviceExpandPanel> getDevicePanel(Container component) {
284         if (component == null) {
285             return Optional.empty();
286         }
287         if (component instanceof DeviceExpandPanel) {
288             DeviceExpandPanel deviceExpandPanel = (DeviceExpandPanel) component;
289             return Optional.ofNullable(deviceExpandPanel);
290         } else {
291             return getDevicePanel(component.getParent());
292         }
293     }
294 
getExpandPanel(Container component)295     private Optional<ExpandPanel> getExpandPanel(Container component) {
296         if (component == null) {
297             return Optional.empty();
298         }
299         if (component instanceof ExpandPanel) {
300             ExpandPanel expandPanel = (ExpandPanel) component;
301             return Optional.ofNullable(expandPanel);
302         } else {
303             return getExpandPanel(component.getParent());
304         }
305     }
306 
307     private class ContentMouseAdapter extends MouseAdapter {
308         @Override
mousePressed(MouseEvent event)309         public void mousePressed(MouseEvent event) {
310             super.mousePressed(event);
311             if (Objects.isNull(componentList)) {
312                 componentList =
313                     Arrays.stream(contentPanel.getComponents()).filter(it -> it instanceof DeviceExpandPanel)
314                         .map(it -> ((DeviceExpandPanel) it))
315                         .flatMap(it -> Arrays.stream(it.getContent().getComponents()))
316                         .filter(it -> it instanceof ExpandPanel).map(it -> ((ExpandPanel) it))
317                         .flatMap(it -> Arrays.stream(it.getContent().getComponents())).collect(Collectors.toList());
318             }
319             if (componentList.size() > 0) {
320                 startPoint = event.getPoint();
321                 componentList.stream().filter(TraceFuncRow.class::isInstance).map(it -> ((TraceFuncRow<?>) it))
322                     .forEach(thread -> {
323                         Rectangle rect =
324                             SwingUtilities.convertRectangle(thread.getParent(), thread.getBounds(), contentPanel);
325                         if (rect.contains(startPoint) && Utils.getX(startPoint) < Utils
326                             .getX(thread.getContentBounds())) {
327                             Optional<DeviceExpandPanel> devicePanel = getDevicePanel(thread);
328                             if (devicePanel.isPresent()) {
329                                 DistributedCache.setCurrentDBFlag(devicePanel.get().getDbFlag());
330                             }
331                         }
332                     });
333             }
334         }
335 
336         @Override
mouseClicked(MouseEvent event)337         public void mouseClicked(MouseEvent event) {
338             super.mouseClicked(event);
339             DistributedTracePanel.rangeStartNS = null;
340             DistributedTracePanel.rangeEndNS = null;
341             AtomicBoolean flag = new AtomicBoolean(false);
342             CURRENT_SELECT_THREAD_IDS.clear();
343             componentList.stream().filter(TraceFuncRow.class::isInstance).map(it -> ((TraceFuncRow<?>) it))
344                 .forEach(thread -> {
345                     Optional<ExpandPanel> exp = getExpandPanel(thread);
346                     Optional<DeviceExpandPanel> dxp = getDevicePanel(thread);
347                     if (exp.isPresent() && dxp.isPresent() && !exp.get().isCollapsed() && !dxp.get().isCollapsed()) {
348                         // Create a rectangle
349                         Rectangle rect =
350                             SwingUtilities.convertRectangle(thread.getParent(), thread.getBounds(), contentPanel);
351 
352                         // If the click coordinates are in the thread name display area, select the thread
353                         if (rect.contains(startPoint) && Utils.getX(startPoint) < Utils
354                             .getX(thread.getContentBounds())) {
355                             if (!CURRENT_SELECT_THREAD_IDS.contains(thread.getTid())) {
356                                 CURRENT_SELECT_THREAD_IDS.add(thread.getTid());
357                             }
358                             thread.setSelect(true, null, null);
359                         } else {
360                             // If the clicked coordinates are on the color block,
361                             // it means that the color block is selected instead of the selected row
362                             Point point =
363                                 SwingUtilities.convertPoint(event.getComponent(), event.getPoint(), thread.content);
364                             if ((Objects.nonNull(thread.getData()) && thread.getData().stream()
365                                 .anyMatch(it -> it.getRect().contains(point)))) {
366                                 thread.getData().stream().filter(it -> it.getRect().contains(point))
367                                     .forEach(it -> it.onClick(event));
368                                 flag.set(true);
369                             }
370                             CURRENT_SELECT_THREAD_IDS.remove(thread.getTid());
371                             thread.setSelect(false, null, null);
372                         }
373                     }
374                 });
375 
376             // The flag value is true to indicate that the click is on the method color block
377             if (!flag.get()) {
378                 notifySelectRangeChange();
379             }
380         }
381 
382         @Override
mouseExited(MouseEvent event)383         public void mouseExited(MouseEvent event) {
384             super.mouseExited(event);
385             Tip.getInstance().hidden();
386         }
387 
388         @Override
mouseReleased(MouseEvent event)389         public void mouseReleased(MouseEvent event) {
390             super.mouseReleased(event);
391             if (isDragRelease) {
392                 notifySelectRangeChange();
393                 isDragRelease = false;
394             }
395         }
396     }
397 
398     private class ContentMouseMotionAdapter extends MouseMotionAdapter {
399         @Override
mouseDragged(MouseEvent event)400         public void mouseDragged(MouseEvent event) {
401             super.mouseDragged(event);
402             isDragRelease = true;
403             endPoint = event.getPoint();
404             int xPoint = Math.min(Utils.getX(startPoint), Utils.getX(endPoint));
405             int yPoint = Math.min(Utils.getY(startPoint), Utils.getY(endPoint));
406             int width = Math.abs(Utils.getX(startPoint) - Utils.getX(endPoint)) == 0 ? 1
407                 : Math.abs(Utils.getX(startPoint) - Utils.getX(endPoint));
408             int height = Math.abs(Utils.getY(startPoint) - Utils.getY(endPoint));
409             Rectangle range = new Rectangle(xPoint, yPoint, width, height);
410             CURRENT_SELECT_THREAD_IDS.clear();
411             componentList.stream().filter(TraceFuncRow.class::isInstance).map(it -> ((TraceFuncRow<?>) it))
412                 .forEach(thread -> {
413                     Optional<ExpandPanel> exp = getExpandPanel(thread);
414                     Optional<DeviceExpandPanel> dxp = getDevicePanel(thread);
415                     if (exp.isPresent() && dxp.isPresent() && !exp.get().isCollapsed() && !dxp.get().isCollapsed()) {
416                         Rectangle rect =
417                             SwingUtilities.convertRectangle(thread.getParent(), thread.getBounds(), contentPanel);
418                         if (range.intersects(rect)) {
419                             if (!CURRENT_SELECT_THREAD_IDS.contains(thread.getTid())) {
420                                 CURRENT_SELECT_THREAD_IDS.add(thread.getTid());
421                             }
422                             thread.setSelect(true, xPoint - Utils.getX(thread.getContentBounds()),
423                                 xPoint + width - Utils.getX(thread.getContentBounds()));
424                         } else {
425                             if (CURRENT_SELECT_THREAD_IDS.contains(thread.getTid())) {
426                                 CURRENT_SELECT_THREAD_IDS.remove(thread.getTid());
427                             }
428                             thread.setSelect(false, null, null);
429                         }
430                     }
431                 });
432             Tip.getInstance().hidden();
433         }
434 
435         @Override
mouseMoved(MouseEvent event)436         public void mouseMoved(MouseEvent event) {
437             super.mouseMoved(event);
438             tip(event);
439         }
440     }
441 
442     private class ContentComponentAdapter extends ComponentAdapter {
443         @Override
componentResized(ComponentEvent event)444         public void componentResized(ComponentEvent event) {
445             super.componentResized(event);
446             if (!isInit) {
447                 isInit = true;
448                 Arrays.stream(contentPanel.getComponents()).filter(DeviceExpandPanel.class::isInstance)
449                     .map(it -> ((DeviceExpandPanel) it)).forEach(it -> it.refresh(startNS, endNS));
450             }
451         }
452     }
453 
getDURATION()454     public static long getDURATION() {
455         return DURATION;
456     }
457 
setDURATION(long DURATION)458     public static void setDURATION(long DURATION) {
459         DistributedTracePanel.DURATION = DURATION;
460     }
461 
getRangeStartNS()462     public static Long getRangeStartNS() {
463         return rangeStartNS;
464     }
465 
setRangeStartNS(Long rangeStartNS)466     public static void setRangeStartNS(Long rangeStartNS) {
467         DistributedTracePanel.rangeStartNS = rangeStartNS;
468     }
469 
getRangeEndNS()470     public static Long getRangeEndNS() {
471         return rangeEndNS;
472     }
473 
setRangeEndNS(Long rangeEndNS)474     public static void setRangeEndNS(Long rangeEndNS) {
475         DistributedTracePanel.rangeEndNS = rangeEndNS;
476     }
477 }
478