• 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.perftrace;
17 
18 import ohos.devtools.views.applicationtrace.DataProcess;
19 import ohos.devtools.views.applicationtrace.bean.AppFunc;
20 import ohos.devtools.views.applicationtrace.bean.TreeTableBean;
21 import ohos.devtools.views.applicationtrace.util.TimeUtils;
22 import ohos.devtools.views.perftrace.bean.PrefFile;
23 import ohos.devtools.views.perftrace.bean.PrefFunc;
24 import ohos.devtools.views.perftrace.bean.PrefRange;
25 import ohos.devtools.views.perftrace.bean.PrefSample;
26 import ohos.devtools.views.trace.util.Utils;
27 
28 import javax.swing.tree.DefaultMutableTreeNode;
29 import javax.swing.tree.TreeNode;
30 import java.util.ArrayList;
31 import java.util.Comparator;
32 import java.util.Enumeration;
33 import java.util.HashMap;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Objects;
37 import java.util.concurrent.TimeUnit;
38 import java.util.function.Consumer;
39 import java.util.stream.Collectors;
40 
41 import static java.util.stream.Collectors.groupingBy;
42 
43 /**
44  * c++ function PerfData
45  *
46  * @since 2021/04/22 12:25
47  */
48 public class PerfData {
49     /**
50      * current Range
51      */
52     private static PrefRange prefRange;
53 
54     /**
55      * current files from db
56      */
57     private static Map<Long, List<PrefFile>> prefFiles;
58 
59     /**
60      * current func map from db
61      */
62     private static Map<Integer, List<PrefFunc>> funcMap = new HashMap<>();
63 
64     /**
65      * current thread name map from db
66      */
67     private static Map<Integer, String> threadNames = new HashMap<>();
68 
69     /**
70      * get the PrefFunc by PrefSample object
71      *
72      * @param sampleList sampleList
73      * @return list PrefFunc
74      */
formatSampleList(List<PrefSample> sampleList)75     public static List<PrefFunc> formatSampleList(List<PrefSample> sampleList) {
76         if (sampleList.size() == 0) {
77             return new ArrayList<>();
78         }
79         Map<Long, List<PrefSample>> sampleMap = sampleList.stream().collect(groupingBy(PrefSample::getSampleId));
80         List<Map.Entry<Long, List<PrefSample>>> collect =
81             sampleMap.entrySet().stream().sorted(Map.Entry.comparingByKey()).collect(Collectors.toList());
82         List<PrefSample> lastEntry = collect.get(0).getValue();
83         PrefFunc threadFunc = new PrefFunc();
84         int threadId = Long.valueOf(sampleList.get(0).getThreadId()).intValue();
85         threadFunc.setFuncName(threadNames.get(threadId));
86         threadFunc.setThreadName(threadNames.get(threadId));
87         threadFunc.setBloodId(Utils.md5String(threadFunc.getFuncName()));
88         threadFunc.setDepth(-1);
89         threadFunc.setStartTs(sampleList.get(0).getTs());
90         int collectIndex = 1;
91         List<PrefFunc> funcList = new ArrayList<>();
92         PrefFunc currentEndNode = addFuncNodes(null, lastEntry, funcList, 0);
93         while (collectIndex < collect.size()) {
94             List<PrefSample> prefSamples = collect.get(collectIndex).getValue().stream()
95                 .sorted(Comparator.comparingLong(PrefSample::getId).reversed()).collect(Collectors.toList());
96             int splitPoint = getSplitPoint(lastEntry, prefSamples);
97             if (splitPoint < lastEntry.size()) {
98                 currentEndNode =
99                     updateParentEndTime(lastEntry.size() - splitPoint, prefSamples.get(0).getTs(), currentEndNode);
100             }
101             if (splitPoint < prefSamples.size()) {
102                 currentEndNode = addFuncNodes(currentEndNode, prefSamples, funcList, splitPoint);
103             }
104             lastEntry = prefSamples;
105             collectIndex++;
106         }
107         if (prefRange != null) {
108             threadFunc.setEndTs(prefRange.getEndTime());
109             threadFunc.setDur(prefRange.getEndTime() - threadFunc.getStartTs());
110             currentEndNode.updateParentEndTime(0, prefRange.getEndTime() - prefRange.getStartTime());
111         }
112         funcList.add(threadFunc);
113         return funcList;
114     }
115 
updateParentEndTime(int index, long ts, PrefFunc node)116     private static PrefFunc updateParentEndTime(int index, long ts, PrefFunc node) {
117         PrefFunc current = node;
118         for (int pos = 0; pos < index; pos++) {
119             current.setEndTs(ts);
120             current = current.getParentNode();
121         }
122         return current;
123     }
124 
getSplitPoint(List<PrefSample> last, List<PrefSample> current)125     private static int getSplitPoint(List<PrefSample> last, List<PrefSample> current) {
126         int index = 0;
127         while (index < last.size() && index < current.size() && last.get(index).isSameStack(current.get(index))) {
128             index++;
129         }
130         return index;
131     }
132 
addFuncNodes(PrefFunc parentNode, List<PrefSample> sampleList, List<PrefFunc> funcList, int startIndex)133     private static PrefFunc addFuncNodes(PrefFunc parentNode, List<PrefSample> sampleList, List<PrefFunc> funcList,
134         int startIndex) {
135         PrefFunc node = parentNode;
136         for (int sampleIndex = startIndex; sampleIndex < sampleList.size(); sampleIndex++) {
137             PrefFunc childNode = new PrefFunc(sampleList.get(sampleIndex));
138             if (node != null) {
139                 childNode.setDepth(node.getDepth() + 1);
140                 childNode.setParentNode(node);
141                 childNode.setParentBloodId(node.getBloodId());
142                 node.getChildrenNodes().add(childNode);
143             } else {
144                 childNode.setDepth(startIndex);
145             }
146             childNode.createBloodId();
147             funcList.add(childNode);
148             node = childNode;
149         }
150         return node;
151     }
152 
153     /**
154      * get the get TopDown FuncTree by startNS and endNS
155      *
156      * @param startNS startNS
157      * @param endNS endNS
158      * @return list nodes
159      */
getFuncTreeTopDown(long startNS, long endNS)160     public static List<DefaultMutableTreeNode> getFuncTreeTopDown(long startNS, long endNS) {
161         if (Objects.isNull(funcMap)) {
162             return new ArrayList<>();
163         }
164         return getFuncTreeTopDown(startNS, endNS, null);
165     }
166 
167     /**
168      * get the get TopDown FuncTree by startNS、endNS and threadIds
169      *
170      * @param startNS startNS
171      * @param endNS endNS
172      * @param threadIds threadIds
173      * @return list nodes
174      */
getFuncTreeTopDown(long startNS, long endNS, List<Integer> threadIds)175     public static List<DefaultMutableTreeNode> getFuncTreeTopDown(long startNS, long endNS, List<Integer> threadIds) {
176         if (Objects.isNull(funcMap)) {
177             return new ArrayList<>();
178         }
179         Map<Integer, List<AppFunc>> collect = funcMap.entrySet().stream()
180             .collect(Collectors.toMap(Map.Entry::getKey, entry -> new ArrayList<>(entry.getValue())));
181         return DataProcess.getFuncTreeTopDown(collect, startNS, endNS, threadIds);
182     }
183 
184     /**
185      * get BottomUp FuncTree data
186      *
187      * @param startNS startNS
188      * @param endNS endNS
189      * @return list TreeTableBean
190      */
getFuncTreeBottomUp(long startNS, long endNS)191     public static List<DefaultMutableTreeNode> getFuncTreeBottomUp(long startNS, long endNS) {
192         return getFuncTreeBottomUp(startNS, endNS, null);
193     }
194 
195     /**
196      * get the get BottomUp FuncTree by startNS、endNS and threadIds
197      *
198      * @param startNS startNS
199      * @param endNS endNS
200      * @param threadIds threadIds
201      * @return list nodes
202      */
getFuncTreeBottomUp(long startNS, long endNS, List<Integer> threadIds)203     public static List<DefaultMutableTreeNode> getFuncTreeBottomUp(long startNS, long endNS, List<Integer> threadIds) {
204         if (Objects.isNull(funcMap)) {
205             return new ArrayList<>();
206         }
207         Map<Integer, List<AppFunc>> collect = funcMap.entrySet().stream()
208             .collect(Collectors.toMap(Map.Entry::getKey, entry -> new ArrayList<>(entry.getValue())));
209         return DataProcess.getFuncTreeBottomUp(collect, startNS, endNS, threadIds);
210     }
211 
212     /**
213      * get the get BottomUp FuncTree by func
214      *
215      * @param func func
216      * @return list nodes
217      */
getFuncTreeByFuncTopDown(PrefFunc func)218     public static List<DefaultMutableTreeNode> getFuncTreeByFuncTopDown(PrefFunc func) {
219         List<PrefFunc> collect = funcMap.get(Long.valueOf(func.getTid()).intValue()).stream().filter(
220             item -> TimeUtils.isRangeCross(func.getStartTs(), func.getEndTs(), item.getStartTs(), item.getEndTs())
221                 && item.getDepth() > func.getDepth()).collect(Collectors.toList());
222         DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
223         rootNode.setUserObject(new TreeTableBean() {
224             {
225                 setName(func.getFuncName());
226                 long totalUs = TimeUnit.NANOSECONDS.toMicros(func.getDur());
227                 setThreadDur(totalUs);
228                 setTotalNum(totalUs);
229                 long threadFuncDuration = TimeUnit.NANOSECONDS.toMicros(collect.stream().filter(
230                     item -> item.getDepth() == func.getDepth() + 1 && TimeUtils
231                         .isRangeCross(func.getStartTs(), func.getEndTs(), item.getStartTs(), item.getEndTs()))
232                     .mapToLong(PrefFunc::getDur).sum());
233                 setChildrenNS(func.getDur());
234                 setChildrenNum(threadFuncDuration);
235                 setSelfNum(totalUs - threadFuncDuration);
236             }
237         });
238         Map<String, TreeTableBean> longTreeTableBeanMap = funcGroupByStackId(func, collect, null);
239         List<TreeTableBean> treeTableBeans = setNumForNodes(longTreeTableBeanMap);
240         Map<String, DefaultMutableTreeNode> treeNodeMap = treeTableBeans.stream()
241             .collect(Collectors.toMap(TreeTableBean::getPrefStackId, DefaultMutableTreeNode::new));
242         treeTableBeans.forEach(listBean -> {
243             if (listBean.getPrefParentStackId().equals(func.getBloodId())) {
244                 rootNode.add(treeNodeMap.get(listBean.getPrefStackId()));
245             } else {
246                 if (treeNodeMap.containsKey(listBean.getPrefParentStackId())) {
247                     treeNodeMap.get(listBean.getPrefParentStackId()).add(treeNodeMap.get(listBean.getPrefStackId()));
248                 }
249             }
250         });
251         List<DefaultMutableTreeNode> objects = new ArrayList<DefaultMutableTreeNode>();
252         objects.add(rootNode);
253         return objects;
254     }
255 
256     /**
257      * get the get BottomUp FuncTree by func
258      *
259      * @param func func
260      * @return list nodes
261      */
getFuncTreeByFuncBottomUp(PrefFunc func)262     public static List<DefaultMutableTreeNode> getFuncTreeByFuncBottomUp(PrefFunc func) {
263         long totalUs = TimeUnit.NANOSECONDS.toMicros(func.getDur());
264         ArrayList<DefaultMutableTreeNode> nodes = new ArrayList<>();
265         List<PrefFunc> collect = funcMap.get(Long.valueOf(func.getTid()).intValue()).stream().filter(
266             item -> TimeUtils.isRangeCross(func.getStartTs(), func.getEndTs(), item.getStartTs(), item.getEndTs()))
267             .collect(Collectors.toList());
268         Map<String, List<String>> nameToId = new HashMap<>();
269         Map<String, TreeTableBean> treeNodeMap = funcGroupByStackId(func, collect, nameToId);
270         setNumForNodes(treeNodeMap);
271         nameToId.forEach((name, ids) -> {
272             DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
273             rootNode.setUserObject(new TreeTableBean(totalUs) {
274                 {
275                     setName(name);
276                     long totalNum = 0L;
277                     long childrenNum = 0L;
278                     long selfNum = 0L;
279                     for (String id : ids) {
280                         TreeTableBean tableBean = treeNodeMap.get(id);
281                         totalNum += tableBean.getTotalNum();
282                         childrenNum += tableBean.getChildrenNum();
283                         selfNum += tableBean.getSelfNum();
284                     }
285                     setTotalNum(totalNum);
286                     setSelfNum(selfNum);
287                     setChildrenNum(childrenNum);
288                 }
289             });
290             ids.forEach(id -> recursionNode(rootNode, treeNodeMap.get(id).getPrefParentStackId(),
291                 threadNames.get(Long.valueOf(func.getTid()).intValue()), treeNodeMap, id));
292             if (ids.stream().noneMatch(id ->
293                 collect.stream().filter(item -> item.getBloodId().equals(id) && item.getDepth() < func.getDepth())
294                     .toArray().length > 0)) {
295                 nodes.add(rootNode);
296             }
297         });
298         return nodes;
299     }
300 
funcGroupByStackId(PrefFunc func, List<PrefFunc> collect, Map<String, List<String>> nameToId)301     private static Map<String, TreeTableBean> funcGroupByStackId(PrefFunc func, List<PrefFunc> collect,
302         Map<String, List<String>> nameToId) {
303         long totalUs = TimeUnit.NANOSECONDS.toMicros(func.getDur());
304         return collect.stream().collect(groupingBy(PrefFunc::getBloodId)).entrySet().stream()
305             .collect(Collectors.toMap(Map.Entry::getKey, entry -> {
306                 TreeTableBean uniteBean = new TreeTableBean(totalUs);
307                 uniteBean.setPrefStackId(entry.getKey());
308                 if (entry.getValue().size() > 0) {
309                     uniteBean.setName(entry.getValue().get(0).getFuncName());
310                     uniteBean.setPrefParentStackId(entry.getValue().get(0).getParentBloodId());
311                     if (nameToId != null) {
312                         if (nameToId.containsKey(entry.getValue().get(0).getFuncName())) {
313                             nameToId.get(entry.getValue().get(0).getFuncName())
314                                 .add(entry.getValue().get(0).getBloodId());
315                         } else {
316                             ArrayList<String> list = new ArrayList<String>();
317                             list.add(entry.getValue().get(0).getBloodId());
318                             nameToId.put(entry.getValue().get(0).getFuncName(), list);
319                         }
320                     }
321                 }
322                 long childrenTotal = entry.getValue().stream().mapToLong(child -> TimeUtils
323                     .getIntersection(func.getStartTs(), func.getEndTs(), child.getStartTs(), child.getEndTs())).sum();
324                 uniteBean.setTotalNum(childrenTotal);
325                 uniteBean.setChildrenNS(entry.getValue().stream().mapToLong(child -> TimeUtils
326                     .getNanoIntersection(func.getStartTs(), func.getEndTs(), child.getStartTs(), child.getEndTs()))
327                     .sum());
328                 return uniteBean;
329             }));
330     }
331 
332     /**
333      * Set up presentation data
334      *
335      * @param map map
336      * @return list TreeTableBean
337      */
setNumForNodes(Map<String, TreeTableBean> map)338     public static List<TreeTableBean> setNumForNodes(Map<String, TreeTableBean> map) {
339         List<TreeTableBean> treeNodes = new ArrayList<>(map.values()); // Sort the array
340         for (TreeTableBean ts : treeNodes) { // Loop set children and total data
341             ts.setSelfNum(ts.getTotalNum() - ts.getChildrenNum());
342             if (map.containsKey(ts.getPrefParentStackId())) {
343                 TreeTableBean mapUserObject = map.get(ts.getPrefParentStackId());
344                 mapUserObject.setChildrenNum(mapUserObject.getChildrenNum() + ts.getTotalNum());
345                 mapUserObject.setSelfNum(mapUserObject.getTotalNum() - mapUserObject.getChildrenNum());
346             }
347         }
348         return treeNodes;
349     }
350 
recursionNode(DefaultMutableTreeNode rootNode, String parentId, String threadName, Map<String, TreeTableBean> treeNodeMap, String id)351     private static void recursionNode(DefaultMutableTreeNode rootNode, String parentId, String threadName,
352         Map<String, TreeTableBean> treeNodeMap, String id) {
353         if (!(rootNode.getUserObject() instanceof TreeTableBean)) {
354             return;
355         }
356         TreeTableBean topBean = (TreeTableBean) rootNode.getUserObject();
357         TreeTableBean timeBean = treeNodeMap.get(id);
358         if (parentId.isEmpty()) { // Leaf node
359             if (rootNode.getChildCount() != 0) { // Merge leaf nodes
360                 if (rootNode.getChildAt(rootNode.getChildCount() - 1) instanceof DefaultMutableTreeNode) {
361                     DefaultMutableTreeNode leafNode =
362                         (DefaultMutableTreeNode) rootNode.getChildAt(rootNode.getChildCount() - 1);
363                     if (leafNode.getUserObject() instanceof TreeTableBean) {
364                         TreeTableBean leafNodeUserObject = (TreeTableBean) leafNode.getUserObject();
365                         leafNodeUserObject.mergeTime(timeBean);
366                         leafNode.setUserObject(leafNodeUserObject);
367                     }
368                 }
369             }
370         } else { // Non-leaf nodes
371             final TreeTableBean idBean = treeNodeMap.get(parentId);
372             boolean sameName = false;
373             Enumeration<TreeNode> enumeration = rootNode.children();
374 
375             // Compare whether there are node names in the current hierarchy that need to be merged
376             while (enumeration.hasMoreElements()) {
377                 DefaultMutableTreeNode nextElement = (DefaultMutableTreeNode) enumeration.nextElement();
378                 if (nextElement.getUserObject() instanceof TreeTableBean) {
379                     TreeTableBean nextElementUserObject = (TreeTableBean) nextElement.getUserObject();
380                     if (nextElementUserObject.getName().equals(idBean.getName())) { // The merge time difference
381                         nextElementUserObject.mergeTime(timeBean);
382                         recursionNode(nextElement, idBean.getPrefParentStackId(), threadName, treeNodeMap, id);
383                         sameName = true;
384                     }
385                 }
386             }
387             if (!sameName) { // No same node needs to be merged
388                 DefaultMutableTreeNode addNode = createNewNode(topBean, timeBean, idBean.getName());
389                 rootNode.add(addNode);
390                 recursionNode(addNode, idBean.getPrefParentStackId(), threadName, treeNodeMap, id);
391             }
392         }
393     }
394 
createNewNode(TreeTableBean topBean, TreeTableBean timeBean, String name)395     private static DefaultMutableTreeNode createNewNode(TreeTableBean topBean, TreeTableBean timeBean, String name) {
396         TreeTableBean treeTableBean = new TreeTableBean(topBean.getThreadDur());
397         treeTableBean.setName(name);
398         treeTableBean.setTime(timeBean);
399         DefaultMutableTreeNode defaultMutableTreeNode = new DefaultMutableTreeNode();
400         defaultMutableTreeNode.setUserObject(treeTableBean);
401         return defaultMutableTreeNode;
402     }
403 
404     /**
405      * get FlameChart data
406      *
407      * @param func func
408      * @return list TreeTableBean
409      */
getFuncTreeFlameChart(PrefFunc func)410     public static List<DefaultMutableTreeNode> getFuncTreeFlameChart(PrefFunc func) {
411         return getFuncTreeByFuncTopDown(func);
412     }
413 
414     /**
415      * get FlameChart data
416      *
417      * @param startNS startNS
418      * @param endNS endNS
419      * @return list nodes
420      */
getFuncTreeFlameChart(long startNS, long endNS)421     public static List<DefaultMutableTreeNode> getFuncTreeFlameChart(long startNS, long endNS) {
422         List<DefaultMutableTreeNode> funcTreeTopDown = getFuncTreeTopDown(startNS, endNS);
423         funcTreeTopDown.forEach(PerfData::resortNode);
424         sortNodeList(funcTreeTopDown);
425         return funcTreeTopDown;
426     }
427 
428     /**
429      * get FlameChart data
430      *
431      * @param startNS startNS
432      * @param endNS endNS
433      * @param threadIds threadIds
434      * @return list nodes
435      */
getFuncTreeFlameChart(long startNS, long endNS, List<Integer> threadIds)436     public static List<DefaultMutableTreeNode> getFuncTreeFlameChart(long startNS, long endNS,
437         List<Integer> threadIds) {
438         List<DefaultMutableTreeNode> funcTreeTopDown = getFuncTreeTopDown(startNS, endNS, threadIds);
439         funcTreeTopDown.forEach(PerfData::resortNode);
440         sortNodeList(funcTreeTopDown);
441         return funcTreeTopDown;
442     }
443 
resortNode(DefaultMutableTreeNode root)444     private static void resortNode(DefaultMutableTreeNode root) {
445         Consumer<DefaultMutableTreeNode> sort = parent -> {
446             Enumeration<TreeNode> children = parent.children();
447             List<DefaultMutableTreeNode> childs = new ArrayList<>();
448             while (children.hasMoreElements()) {
449                 TreeNode node = children.nextElement();
450                 if (node instanceof DefaultMutableTreeNode) {
451                     childs.add((DefaultMutableTreeNode) node);
452                 }
453             }
454             parent.removeAllChildren();
455             sortNodeList(childs).forEach(parent::add);
456         };
457         Enumeration enumeration = root.depthFirstEnumeration();
458         while (enumeration.hasMoreElements() && enumeration.nextElement() instanceof DefaultMutableTreeNode) {
459             DefaultMutableTreeNode node = (DefaultMutableTreeNode) enumeration.nextElement();
460             if (node != null) {
461                 if (!node.isLeaf() && node.getChildCount() > 1) {
462                     sort.accept(node);
463                 }
464             }
465         }
466     }
467 
sortNodeList(List<DefaultMutableTreeNode> list)468     private static List<DefaultMutableTreeNode> sortNodeList(List<DefaultMutableTreeNode> list) {
469         return list.stream().sorted((child1, child2) -> {
470             if (child1.getUserObject() instanceof TreeTableBean && child2.getUserObject() instanceof TreeTableBean) {
471                 TreeTableBean bean1 = (TreeTableBean) child1.getUserObject();
472                 TreeTableBean bean2 = (TreeTableBean) child2.getUserObject();
473                 return Long.compare(bean2.getTotalNum(), bean1.getTotalNum());
474             }
475             return 0;
476         }).collect(Collectors.toList());
477     }
478 
479     /**
480      * clear all static data
481      */
482     public static void clearData() {
483         if (prefRange != null) {
484             prefRange = null;
485         }
486         if (funcMap != null) {
487             if (funcMap.size() > 0) {
488                 funcMap.values().forEach(List::clear);
489             }
490             funcMap.clear();
491         }
492         if (threadNames != null) {
493             threadNames.clear();
494         }
495         if (prefFiles != null) {
496             prefFiles.clear();
497         }
498     }
499 
500     public static PrefRange getPrefRange() {
501         return prefRange;
502     }
503 
504     public static void setPrefRange(PrefRange prefRange) {
505         PerfData.prefRange = prefRange;
506     }
507 
508     public static Map<Long, List<PrefFile>> getPrefFiles() {
509         return prefFiles;
510     }
511 
512     public static void setPrefFiles(Map<Long, List<PrefFile>> prefFiles) {
513         PerfData.prefFiles = prefFiles;
514     }
515 
516     public static Map<Integer, List<PrefFunc>> getFuncMap() {
517         return funcMap;
518     }
519 
520     public static void setFuncMap(Map<Integer, List<PrefFunc>> funcMap) {
521         PerfData.funcMap = funcMap;
522     }
523 
524     public static Map<Integer, String> getThreadNames() {
525         return threadNames;
526     }
527 
528     public static void setThreadNames(Map<Integer, String> threadNames) {
529         PerfData.threadNames = threadNames;
530     }
531 }
532