• 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.trace.component;
17 
18 import com.intellij.ui.JBColor;
19 import com.intellij.ui.components.JBPanel;
20 import com.intellij.ui.components.JBScrollPane;
21 import ohos.devtools.views.trace.Common;
22 import ohos.devtools.views.trace.bean.CpuData;
23 import ohos.devtools.views.trace.bean.WakeupBean;
24 import ohos.devtools.views.trace.fragment.AbstractDataFragment;
25 import ohos.devtools.views.trace.fragment.CpuDataFragment;
26 import ohos.devtools.views.trace.fragment.FunctionDataFragment;
27 import ohos.devtools.views.trace.fragment.MemDataFragment;
28 import ohos.devtools.views.trace.fragment.ProcessDataFragment;
29 import ohos.devtools.views.trace.fragment.ThreadDataFragment;
30 import ohos.devtools.views.trace.fragment.ruler.AbstractNode;
31 import ohos.devtools.views.trace.util.Final;
32 import ohos.devtools.views.trace.util.TimeUtils;
33 import ohos.devtools.views.trace.util.Utils;
34 
35 import javax.swing.JViewport;
36 import javax.swing.SwingUtilities;
37 import java.awt.BasicStroke;
38 import java.awt.Color;
39 import java.awt.Dimension;
40 import java.awt.Graphics;
41 import java.awt.Graphics2D;
42 import java.awt.Point;
43 import java.awt.Rectangle;
44 import java.awt.RenderingHints;
45 import java.util.ArrayList;
46 import java.util.List;
47 import java.util.Objects;
48 import java.util.Optional;
49 import java.util.stream.Collectors;
50 
51 /**
52  * Rolling container
53  *
54  * @since 2021/04/20 12:24
55  */
56 public final class ContentPanel extends JBPanel implements AbstractDataFragment.IDataFragment {
57     /**
58      * clint fragment
59      */
60     public static AbstractDataFragment clickFragment;
61 
62     /**
63      * FragmentList to be rendered
64      */
65     public final List<AbstractDataFragment> fragmentList = new ArrayList<>();
66 
67     /**
68      * start point object
69      */
70     protected Point startPoint;
71 
72     /**
73      * end point object
74      */
75     protected Point endPoint;
76 
77     /**
78      * Analysis component
79      */
80     protected AnalystPanel analystPanel;
81 
82     /**
83      * draw range select flag
84      */
85     protected boolean drawRangeSelect;
86 
87     /**
88      * draw range select data flag
89      */
90     protected boolean drawRangeSelectData;
91 
92     /**
93      * range select x1
94      */
95     protected int x1;
96 
97     /**
98      * range select y1
99      */
100     protected int y1;
101 
102     /**
103      * range select x2
104      */
105     protected int x2;
106 
107     /**
108      * range select y2
109      */
110     protected int y2;
111 
112     /**
113      * range start time
114      */
115     protected long rangeStartNS;
116 
117     /**
118      * range end time
119      */
120     protected long rangeEndNS;
121 
122     private WakeupBean wakeupBean;
123     private long startNS;
124     private long endNS;
125     private final BasicStroke boldStoke = new BasicStroke(2);
126     private final BasicStroke normalStoke = new BasicStroke(1);
127 
128     /**
129      * Constructor
130      *
131      * @param analystPanel component
132      */
ContentPanel(AnalystPanel analystPanel)133     public ContentPanel(AnalystPanel analystPanel) {
134         this.analystPanel = analystPanel;
135         this.setOpaque(false);
136         setFont(Final.NORMAL_FONT);
137     }
138 
139     /**
140      * Gets the value of wakeupBean .
141      *
142      * @return the value of ohos.devtools.views.trace.bean.WakeupBean
143      */
getWakeupBean()144     public WakeupBean getWakeupBean() {
145         return wakeupBean;
146     }
147 
148     /**
149      * Sets the wakeupBean .
150      * <p>You can use getWakeupBean() to get the value of wakeupBean</p>
151      *
152      * @param wakeup wakeup
153      */
setWakeupBean(WakeupBean wakeup)154     public void setWakeupBean(WakeupBean wakeup) {
155         this.wakeupBean = wakeup;
156     }
157 
158     @Override
paintComponent(Graphics graphics)159     protected void paintComponent(Graphics graphics) {
160         super.paintComponent(graphics);
161         if (graphics instanceof Graphics2D) {
162             Graphics2D g2 = (Graphics2D) graphics;
163             g2.setFont(Final.NORMAL_FONT);
164             g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
165             if (getParent() instanceof TimeViewPort) {
166                 TimeViewPort parent = (TimeViewPort) getParent();
167                 Rectangle viewRect = parent.getViewRect();
168                 // Render the line in the viewport display area, and the line beyond the range will not be rendered
169                 fragmentList.stream().filter(fragment -> {
170                     // Cancel the future if the fragment is hidden
171                     if (!fragment.visible) {
172                         fragment.cancel();
173                     }
174                     return fragment.visible;
175                 }).filter(fragment -> {
176                     if (fragment != null && fragment.getRect() != null && viewRect != null) {
177                         boolean rs = Utils.getY(fragment.getRect()) + fragment.getRect().height
178                             >= Utils.getY(viewRect) + TimeViewPort.height
179                             && Utils.getY(fragment.getRect()) <= Utils.getY(viewRect) + viewRect.height;
180                         if (rs) {
181                             return true;
182                         } else {
183                             // If it is not in the display area, cancel the future
184                             fragment.cancel();
185                             return false;
186                         }
187                     } else {
188                         return false;
189                     }
190                 }).forEach(fragment -> {
191                     if (fragment != null) {
192                         fragment.draw(g2);
193                     }
194                 });
195             }
196             drawRangeSelect(g2);
197             drawWakeup(g2);
198         }
199     }
200 
drawRangeSelect(Graphics2D g2)201     private void drawRangeSelect(Graphics2D g2) {
202         if (Objects.nonNull(startPoint) && Objects.nonNull(endPoint)) {
203             var ref = new Object() {
204                 int realY1;
205                 int realY2;
206             };
207             fragmentList.stream().filter(it -> it.visible).filter(it -> it.getDataRect().contains(x1, y1))
208                 .forEach(it -> ref.realY1 = Utils.getY(it.getRect()));
209             fragmentList.stream().filter(it -> it.visible).filter(it -> it.getDataRect().contains(x2 - 1, y2 - 1))
210                 .forEach(it -> ref.realY2 = Utils.getY(it.getRect()) + it.getRect().height);
211             int tmpWidth = Math.abs(x2 - x1);
212             int tmpHeight = Math.abs(ref.realY2 - ref.realY1);
213             Rectangle range = new Rectangle(x1, ref.realY1, tmpWidth, tmpHeight);
214             if (drawRangeSelect) {
215                 g2.setStroke(new BasicStroke(2));
216                 g2.setColor(JBColor.foreground().brighter());
217                 g2.drawRect(Utils.getX(range), Utils.getY(range), range.width, range.height);
218             } else {
219                 Common.setAlpha(g2, 0.5F);
220                 g2.setColor(JBColor.foreground().brighter());
221                 g2.fillRect(Utils.getX(range), Utils.getY(range), range.width, range.height);
222                 if (drawRangeSelectData) {
223                     drawRangeSelectData = false;
224                     List<AbstractDataFragment> rangeFragments =
225                         fragmentList.stream().filter(it -> range.intersects(it.getDataRect()))
226                             .collect(Collectors.toList());
227                     // Draw after dragging
228                     rangeFragments.forEach(it -> it.drawFrame());
229                     List<AbstractDataFragment> process = new ArrayList<>();
230                     rangeFragments.stream().filter(it -> it instanceof ProcessDataFragment)
231                         .map(it -> (ProcessDataFragment) it).forEach(it -> {
232                         process.addAll(fragmentList.stream().filter(that -> that.parentUuid.equals(it.uuid))
233                             .collect(Collectors.toList()));
234                     });
235                     rangeFragments.addAll(process);
236                     List<Integer> cpu = rangeFragments.stream().filter(it -> it instanceof CpuDataFragment)
237                         .map(it -> ((CpuDataFragment) it).getIndex()).collect(Collectors.toList());
238                     List<Integer> threads = rangeFragments.stream().filter(it -> it instanceof ThreadDataFragment)
239                         .map(it -> ((ThreadDataFragment) it).thread.getTid()).collect(Collectors.toList());
240                     List<Integer> tracks = rangeFragments.stream().filter(it -> it instanceof MemDataFragment)
241                         .map(it -> ((MemDataFragment) it).mem.getTrackId()).collect(Collectors.toList());
242                     List<Integer> functions = rangeFragments.stream().filter(it -> it instanceof FunctionDataFragment)
243                         .map(it -> ((FunctionDataFragment) it).thread.getTid()).collect(Collectors.toList());
244                     AnalystPanel.LeftRightNS ns = new AnalystPanel.LeftRightNS();
245                     ns.setLeftNs(rangeStartNS);
246                     ns.setRightNs(rangeEndNS);
247                     this.analystPanel.boxSelection(cpu, threads, tracks, functions, ns);
248                 }
249             }
250             Common.setAlpha(g2, 1.0F);
251         }
252     }
253 
254     /**
255      * add data line
256      *
257      * @param fragment data fragment
258      */
addDataFragment(AbstractDataFragment fragment)259     public void addDataFragment(AbstractDataFragment fragment) {
260         fragment.setDataFragmentListener(this);
261         fragmentList.add(fragment);
262     }
263 
264     /**
265      * add data line
266      *
267      * @param index line index
268      * @param fragment data fragment
269      */
addDataFragment(int index, AbstractDataFragment fragment)270     public void addDataFragment(int index, AbstractDataFragment fragment) {
271         fragment.setDataFragmentListener(this);
272         fragmentList.add(index, fragment);
273     }
274 
275     /**
276      * refresh content data
277      */
refresh()278     public void refresh() {
279         List<AbstractDataFragment> fs =
280             fragmentList.stream().filter(fragment -> fragment.visible).collect(Collectors.toList());
281         int timeViewHeight = TimeViewPort.height;
282         for (int index = 0, len = fs.size(); index < len; index++) {
283             AbstractDataFragment dataFragment = fs.get(index);
284             timeViewHeight += dataFragment.defaultHeight;
285             dataFragment.getRect().height = dataFragment.defaultHeight;
286             dataFragment.getDescRect().height = dataFragment.defaultHeight;
287             dataFragment.getDataRect().height = dataFragment.defaultHeight;
288             Utils.setY(dataFragment.getRect(), timeViewHeight - dataFragment.defaultHeight);
289             Utils.setY(dataFragment.getDescRect(), timeViewHeight - dataFragment.defaultHeight);
290             Utils.setY(dataFragment.getDataRect(), timeViewHeight - dataFragment.defaultHeight);
291             Utils.setX(dataFragment.getRect(), 0);
292             Utils.setX(dataFragment.getDescRect(), 0);
293             Utils.setX(dataFragment.getDataRect(), 200);
294         }
295         Dimension dim = new Dimension(0, timeViewHeight);
296         this.setPreferredSize(dim);
297         this.setSize(dim);
298         this.setMaximumSize(dim);
299         repaint();
300     }
301 
302     /**
303      * recycle content data
304      */
recycle()305     public void recycle() {
306         clickFragment = null;
307         if (fragmentList != null) {
308             fragmentList.forEach(AbstractDataFragment::recycle);
309             fragmentList.clear();
310         }
311     }
312 
313     /**
314      * time range change will call this
315      *
316      * @param startNS range start ns
317      * @param endNS range end ns
318      */
rangeChange(long startNS, long endNS)319     public void rangeChange(long startNS, long endNS) {
320         this.startNS = startNS;
321         this.endNS = endNS;
322         x1 = (int) getRangeX(rangeStartNS) + 200;
323         x2 = (int) getRangeX(rangeEndNS) + 200;
324         fragmentList.forEach(fragment -> fragment.range(startNS, endNS));
325     }
326 
getRangeX(long range)327     private double getRangeX(long range) {
328         double xSize = (range - startNS) * (getWidth() - 200) / (endNS - startNS);
329         if (xSize < 0) {
330             xSize = 0;
331         }
332         if (xSize > getWidth() - 200) {
333             xSize = getWidth() - 200;
334         }
335         return xSize;
336     }
337 
338     @Override
collect(AbstractDataFragment fgr)339     public void collect(AbstractDataFragment fgr) {
340         if (getParent() instanceof TimeViewPort) {
341             TimeViewPort viewPort = (TimeViewPort) getParent();
342             if (fgr.visible) {
343                 viewPort.favorite(fgr);
344             } else {
345                 viewPort.cancel(fgr);
346             }
347             refresh();
348         }
349     }
350 
351     @Override
check(AbstractDataFragment fgr)352     public void check(AbstractDataFragment fgr) {
353     }
354 
drawWakeup(Graphics2D graphics)355     private void drawWakeup(Graphics2D graphics) {
356         if (clickFragment == null || CpuDataFragment.currentSelectedCpuData == null || !AnalystPanel.clicked) {
357             return;
358         }
359         Optional.ofNullable(wakeupBean).ifPresent(wakeup -> {
360             graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
361             graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
362             graphics.setColor(Color.BLACK);
363             graphics.setStroke(boldStoke);
364             Rectangle visibleRect = getVisibleRect();
365             Rectangle dataRect = clickFragment.getDataRect();
366             int wakeupX = clickFragment.getX(wakeup.getWakeupTime());
367             if (wakeup.getWakeupTime() > startNS && wakeup.getWakeupTime() < endNS) {
368                 /**
369                  * Draw the vertical line
370                  */
371                 graphics
372                     .drawLine(wakeupX + Utils.getX(dataRect), Utils.getY(visibleRect), wakeupX + Utils.getX(dataRect),
373                         Utils.getY(visibleRect) + visibleRect.height);
374                 /**
375                  * Draw a diamond First filter out which cpu fragment the wake-up thread is in,
376                  * get the rect of the cpu fragment, and then calculate the coordinates of the drawing diamond
377                  */
378                 for (AbstractDataFragment dataFragment : fragmentList) {
379                     if (dataFragment instanceof CpuDataFragment) {
380                         if (((CpuDataFragment) dataFragment).getIndex() == wakeup.getWakeupCpu()) {
381                             Rectangle wakeCPURect = dataFragment.getRect();
382                             final int[] xs = {Utils.getX(dataRect) + wakeupX, Utils.getX(dataRect) + wakeupX + 6,
383                                 Utils.getX(dataRect) + wakeupX, Utils.getX(dataRect) + wakeupX - 6};
384                             final int[] ys = {Utils.getY(wakeCPURect) + wakeCPURect.height / 2 - 10,
385                                 Utils.getY(wakeCPURect) + wakeCPURect.height / 2,
386                                 Utils.getY(wakeCPURect) + wakeCPURect.height / 2 + 10,
387                                 Utils.getY(wakeCPURect) + wakeCPURect.height / 2};
388                             graphics.fillPolygon(xs, ys, xs.length);
389                             break;
390                         }
391                     }
392                 }
393             }
394             drawArrayAndText(graphics, dataRect, wakeupX, wakeup);
395         });
396     }
397 
drawArrayAndText(Graphics2D graphics, Rectangle dataRect, int wakeupX, WakeupBean wakeup)398     private void drawArrayAndText(Graphics2D graphics, Rectangle dataRect, int wakeupX, WakeupBean wakeup) {
399         /**
400          * Draw arrows and text
401          */
402         CpuData selectCpu = CpuDataFragment.currentSelectedCpuData;
403         if (selectCpu != null) {
404             Rectangle rectangle =
405                 new Rectangle(wakeupX + Utils.getX(dataRect), Utils.getY(selectCpu.rect) + selectCpu.rect.height / 2,
406                     Utils.getX(selectCpu.rect) - wakeupX - Utils.getX(dataRect), 30);
407             if (selectCpu.getStartTime() > startNS && wakeup.getWakeupTime() < endNS) {
408                 graphics
409                     .drawLine(Utils.getX(dataRect) + wakeupX, Utils.getY(selectCpu.rect) + selectCpu.rect.height - 2,
410                         Utils.getX(selectCpu.rect), Utils.getY(selectCpu.rect) + selectCpu.rect.height - 2);
411             }
412             if (rectangle.width > 10) {
413                 if (wakeup.getWakeupTime() > startNS && wakeup.getWakeupTime() < endNS) {
414                     drawArrow(graphics, Utils.getX(dataRect) + wakeupX,
415                         Utils.getY(selectCpu.rect) + selectCpu.rect.height - 2, -1);
416                 }
417                 if (selectCpu.getStartTime() < endNS && selectCpu.getStartTime() > startNS) {
418                     drawArrow(graphics, Utils.getX(selectCpu.rect),
419                         Utils.getY(selectCpu.rect) + selectCpu.rect.height - 2, 1);
420                 }
421             }
422             if (wakeup.getWakeupTime() < endNS && selectCpu.getStartTime() > startNS) {
423                 long offsetTime = selectCpu.getStartTime() - wakeup.getWakeupTime();
424                 String timeString = TimeUtils.getTimeString(offsetTime);
425                 Utils.setY(rectangle, Utils.getY(rectangle) - 5);
426                 selectCpu.drawString(graphics, rectangle, timeString, AbstractNode.Placement.CENTER);
427             }
428         }
429         graphics.setStroke(normalStoke);
430     }
431 
432     /**
433      * repaint panel
434      */
clearWakeupAndBoxSelect()435     public void clearWakeupAndBoxSelect() {
436         if (Objects.nonNull(startPoint) || Objects.nonNull(endPoint) || Objects.nonNull(wakeupBean)) {
437             drawRangeSelect = false;
438             drawRangeSelectData = false;
439             startPoint = null;
440             endPoint = null;
441             repaint();
442         }
443     }
444 
drawArrow(Graphics2D graphics, int xVal, int yVal, int align)445     private void drawArrow(Graphics2D graphics, int xVal, int yVal, int align) {
446         if (align == -1) {
447             final int[] xArray = {xVal, xVal + 5, xVal + 5};
448             final int[] yArray = {yVal, yVal - 5, yVal + 5};
449             graphics.fillPolygon(xArray, yArray, xArray.length);
450         }
451         if (align == 1) {
452             final int[] xArray = {xVal, xVal - 5, xVal - 5};
453             final int[] yArray = {yVal, yVal - 5, yVal + 5};
454             graphics.fillPolygon(xArray, yArray, xArray.length);
455         }
456     }
457 
458     /**
459      * Jump to the cpu line and select the node where startTime starts
460      *
461      * @param cpu cpu
462      * @param startTime startTime
463      */
scrollToCpu(int cpu, long startTime)464     public void scrollToCpu(int cpu, long startTime) {
465         fragmentList.stream().filter(CpuDataFragment.class::isInstance).map(it -> ((CpuDataFragment) it))
466             .filter(it -> it.getIndex() == cpu).findFirst().ifPresent(it -> {
467             if (getParent() instanceof JViewport) {
468                 JViewport parent = (JViewport) getParent();
469                 if (it.getData() != null) {
470                     it.getData().stream().filter(cpuData -> cpuData.getStartTime() == startTime).findFirst()
471                         .ifPresent(cpuData -> it.click(null, cpuData));
472                 } else {
473                     it.delayClickStartTime = startTime;
474                 }
475                 JBScrollPane scrollPane = (JBScrollPane) parent.getParent();
476                 scrollPane.getVerticalScrollBar().setValue(0);
477             }
478         });
479     }
480 
481     /**
482      * Jump to the specified location
483      *
484      * @param processId processId
485      * @param processName processName
486      * @param tid tid
487      * @param startTime startTime
488      * @param offsetHeight tab height
489      */
scrollToThread(int processId, String processName, int tid, long startTime, int offsetHeight)490     public void scrollToThread(int processId, String processName, int tid, long startTime, int offsetHeight) {
491         fragmentList.stream().filter(ProcessDataFragment.class::isInstance).map(it -> ((ProcessDataFragment) it))
492             .filter(it -> it.getProcess().getPid() == processId).findFirst().ifPresent(it -> {
493             if (getParent() instanceof JViewport) {
494                 JViewport parent = (JViewport) getParent();
495                 it.expandThreads();
496 
497                 SwingUtilities.invokeLater(() -> {
498                     fragmentList.stream().filter(ThreadDataFragment.class::isInstance)
499                         .map(th -> ((ThreadDataFragment) th))
500                         .filter(th -> th.thread.getTid() == tid && th.parentUuid.equals(it.uuid)).findFirst()
501                         .ifPresent(th -> {
502                             // If the thread data has been loaded, data is not empty,
503                             // find the node whose start time is startTime, and select it
504                             if (th.getData() != null) {
505                                 th.getData().stream().filter(threadData -> threadData.getStartTime() == startTime)
506                                     .findFirst().ifPresent(threadData -> {
507                                     th.click(null, threadData);
508                                 });
509                             } else {
510                                 // If the thread data has not been loaded yet,
511                                 // save the start time of the node to be selected,
512                                 // the load Data() method in the component will process this field,
513                                 // select it directly after loading,
514                                 // and then set the delay Click Start Time to null
515                                 th.delayClickStartTime = startTime;
516                                 th.setDelayClickProcessName(processName);
517                             }
518                             parent.scrollRectToVisible(
519                                 new Rectangle(Utils.getX(th.getRect()), Utils.getY(th.getRect()) + offsetHeight,
520                                     th.getRect().width, th.getRect().height));
521                         });
522                 });
523             }
524         });
525     }
526 }
527