1/* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17import {byteArrayToString} from 'common/buffer_utils'; 18import {StringUtils} from 'common/string_utils'; 19import {Timestamp} from 'common/time/time'; 20import {AbstractParser} from 'parsers/legacy/abstract_parser'; 21import {TraceType} from 'trace/trace_type'; 22import {PropertyTreeBuilderFromProto} from 'trace/tree_node/property_tree_builder_from_proto'; 23import {PropertyTreeNode} from 'trace/tree_node/property_tree_node'; 24 25class ParserEventLog extends AbstractParser<PropertyTreeNode, Event> { 26 private static readonly MAGIC_NUMBER_STRING = 'EventLog'; 27 private static readonly MAGIC_NUMBER: number[] = Array.from( 28 new TextEncoder().encode(ParserEventLog.MAGIC_NUMBER_STRING), 29 ); 30 31 override getTraceType(): TraceType { 32 return TraceType.EVENT_LOG; 33 } 34 35 override getMagicNumber(): number[] { 36 return ParserEventLog.MAGIC_NUMBER; 37 } 38 39 override getRealToMonotonicTimeOffsetNs(): bigint | undefined { 40 return undefined; 41 } 42 43 override getRealToBootTimeOffsetNs(): bigint | undefined { 44 return undefined; 45 } 46 47 override decodeTrace(buffer: Uint8Array): Event[] { 48 const decodedLogs = this.decodeByteArray(buffer); 49 const events = this.parseLogs(decodedLogs); 50 return events.sort((a: Event, b: Event) => { 51 return a.eventTimestamp < b.eventTimestamp ? -1 : 1; 52 }); 53 } 54 55 protected override getTimestamp(entry: Event): Timestamp { 56 return this.timestampConverter.makeTimestampFromRealNs( 57 entry.eventTimestamp, 58 ); 59 } 60 61 override processDecodedEntry(index: number, entry: Event): PropertyTreeNode { 62 return new PropertyTreeBuilderFromProto() 63 .setData(entry) 64 .setRootId('EventLogTrace') 65 .setRootName('event') 66 .build(); 67 } 68 69 private decodeByteArray(bytes: Uint8Array): string[] { 70 const allLogsString = byteArrayToString(bytes); 71 const splitLogs = allLogsString.split('\n'); 72 73 const firstIndexOfEventLogTrace = splitLogs.findIndex((substring) => { 74 return ( 75 !substring.includes(ParserEventLog.MAGIC_NUMBER_STRING) && 76 !substring.includes('beginning of events') && 77 !StringUtils.isBlank(substring) 78 ); 79 }); 80 81 const lastIndexOfEventLogTrace = splitLogs.findIndex((substring, index) => { 82 return ( 83 index > firstIndexOfEventLogTrace && StringUtils.isBlank(substring) 84 ); 85 }); 86 87 if (lastIndexOfEventLogTrace === -1) { 88 return splitLogs.slice(firstIndexOfEventLogTrace); 89 } 90 return splitLogs.slice(firstIndexOfEventLogTrace, lastIndexOfEventLogTrace); 91 } 92 93 private parseLogs(input: string[]): Event[] { 94 return input.map((log) => { 95 const [metaData, eventData] = log 96 .split(':', 2) 97 .map((string) => string.trim()); 98 const [rawTimestamp, uid, pid, tid, priority, tag] = metaData 99 .split(' ') 100 .filter((substring) => substring.length > 0); 101 const timestampNs = BigInt(rawTimestamp.replace('.', '')); 102 return { 103 eventTimestamp: timestampNs, 104 pid: Number(pid), 105 uid: Number(uid), 106 tid: Number(tid), 107 tag, 108 eventData, 109 }; 110 }); 111 } 112} 113 114interface Event { 115 eventTimestamp: bigint; 116 pid: number; 117 uid: number; 118 tid: number; 119 tag: string; 120 eventData: string; 121} 122 123export {ParserEventLog}; 124