1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3 4""" 5Copyright (c) 2025 Huawei Device Co., Ltd. 6Licensed under the Apache License, Version 2.0 (the "License"); 7you may not use this file except in compliance with the License. 8You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12Unless required by applicable law or agreed to in writing, software 13distributed under the License is distributed on an "AS IS" BASIS, 14WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15See the License for the specific language governing permissions and 16limitations under the License. 17""" 18 19from collections import defaultdict 20import json 21import logging 22import os 23import stat 24 25# Configure logging to display timestamps and messages 26logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(message)s") 27 28 29def gen_parent(data): 30 """ 31 Generate parent information for each event in the data. 32 This function sorts the events by timestamp and assigns a parent to each event 33 based on the conditions that the parent event must fully contain the current event 34 in terms of start time (ts) and duration (dur), and share the same process (pid) and thread (tid). 35 """ 36 # Sort the data by timestamp (ts) 37 data = sorted(data, key=lambda x: x["ts"]) 38 data_length = len(data) 39 for i in range(data_length): 40 current = data[i] # Current event 41 current_end = current["ts"] + current["dur"] # End time of the current event 42 for j in range(i - 1, -1, -1): # Candidate parent event 43 candidate = data[j] 44 if candidate is None: 45 continue 46 candidate_end = candidate["ts"] + candidate["dur"] 47 # Check if the candidate event fully contains the current event 48 if ( 49 candidate["ts"] <= current["ts"] 50 and candidate_end >= current_end 51 and candidate["pid"] == current["pid"] 52 and candidate["tid"] == current["tid"] 53 ): 54 # Assign the candidate event as the parent of the current event 55 current["parent"] = candidate["name"] 56 break 57 if "parent" in current and current["name"] == current["parent"]: 58 data[i] = None 59 60 data = [event for event in data if event is not None] 61 return data 62 63 64def re_gen_ts_and_dur(data_with_parent): 65 """ 66 Regenerate timestamps (ts) and durations (dur) for events based on their parent relationships. 67 This function aggregates durations for events with the same parent and name, 68 and adjusts their timestamps to ensure correct ordering. 69 """ 70 # Dictionary to store aggregated data 71 data_with_dur = {} 72 for event in data_with_parent: 73 name = event["name"] 74 parent = event.get("parent") 75 key = (parent, name) # Use parent and name as a composite key 76 ts = int(event["ts"]) 77 dur = int(event["dur"]) 78 end = ts + dur 79 if key not in data_with_dur: 80 # Initialize the entry if the key doesn't exist 81 data_with_dur[key] = { 82 **event, 83 "ts": ts, 84 "dur": dur, 85 "end": end, 86 } 87 else: 88 # Aggregate durations for events with the same parent and name 89 data_with_dur[key]["dur"] += dur 90 data_with_dur[key]["end"] = ( 91 data_with_dur[key]["ts"] + data_with_dur[key]["dur"] 92 ) 93 94 # Convert the dictionary back to a list and sort by timestamp 95 re_data = list(data_with_dur.values()) 96 re_data.sort(key=lambda x: x["ts"]) 97 98 # Group events by their parent 99 groups = group_by_parent(re_data) 100 101 def get_parent_ts(parent_name, re_data): 102 """ 103 Helper function to get the start time (ts) of a parent event. 104 Handles cases where the parent is a root node (no parent) or a non-root node. 105 """ 106 if not parent_name: 107 return None # Root node case 108 for event in re_data: 109 # Get the start time of the root node 110 if event["name"] == parent_name and "parent" not in event: 111 return event["ts"] 112 # Get the start time of a non-root node as a parent 113 for event in re_data: 114 if event["name"] == parent_name: 115 return event["ts"] 116 return None 117 118 # Flatten the grouped data and adjust timestamps based on parent relationships 119 flattened = [] 120 for parent_name, nodes in groups.items(): 121 if not nodes: 122 continue 123 nodes.sort(key=lambda x: x["ts"]) 124 parent_ts = get_parent_ts(parent_name, re_data) 125 if parent_ts is not None: 126 # Adjust timestamps based on the parent's start time 127 nodes[0]["ts"] = parent_ts 128 nodes[0]["end"] = nodes[0]["ts"] + nodes[0]["dur"] 129 for i in range(1, len(nodes)): 130 nodes[i]["ts"] = nodes[i - 1]["end"] 131 nodes[i]["end"] = nodes[i]["ts"] + nodes[i]["dur"] 132 else: 133 # Adjust timestamps sequentially if no parent start time is available 134 current_end = nodes[0]["ts"] + nodes[0]["dur"] 135 for i in range(1, len(nodes)): 136 nodes[i]["ts"] = current_end 137 current_end = nodes[i]["ts"] + nodes[i]["dur"] 138 nodes[i]["end"] = current_end 139 flattened.extend(nodes) 140 141 # Save the flattened data to a JSON file 142 if os.path.exists('data.json'): 143 os.remove('data.json') 144 flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL 145 modes = stat.S_IWUSR | stat.S_IRUSR 146 with os.fdopen(os.open('data.json', flags, modes), 'w') as file: 147 json.dump(flattened, file, indent=2) 148 return groups 149 150 151def group_by_parent(data): 152 """ 153 Group events by their parent. 154 Events with the same parent are grouped together and sorted by timestamp. 155 """ 156 groups = defaultdict(list) 157 for node in data: 158 parent = node.get("parent") 159 groups[parent].append(node) 160 for parent in groups: 161 groups[parent].sort(key=lambda x: x["ts"]) 162 return dict(groups) 163 164 165if __name__ == "__main__": 166 # Load input data from a JSON file 167 with open("timePerformanceData.json", "r") as f: 168 datas = json.load(f) 169 # Generate parent information for each event 170 data_with_parents = gen_parent(datas) 171 # Regenerate timestamps and durations based on parent relationships 172 re_gen_ts_and_dur(data_with_parents) 173