• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2024 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 */
15class stateMgmtDFX {
16  // enable profile
17  public static enableProfiler: boolean = false;
18  public static inRenderingElementId: Array<number> = new Array<number>();
19  private static readonly DUMP_MAX_PROPERTY_COUNT: number = 50;
20  private static readonly DUMP_MAX_LENGTH: number = 10;
21  private static readonly DUMP_LAST_LENGTH: number = 3;
22  public static enableDebug: boolean = false;
23
24  public static getObservedPropertyInfo<T>(observedProp: ObservedPropertyAbstractPU<T>, isProfiler: boolean,
25    changedTrackPropertyName?: string): ObservedPropertyInfo<T> {
26    return {
27      decorator: observedProp.debugInfoDecorator(), propertyName: observedProp.info(), id: observedProp.id__(),
28      changedTrackPropertyName: changedTrackPropertyName,
29      value: stateMgmtDFX.getRawValue(observedProp),
30      inRenderingElementId: stateMgmtDFX.inRenderingElementId.length === 0 ?
31        -1 : stateMgmtDFX.inRenderingElementId[stateMgmtDFX.inRenderingElementId.length - 1],
32      dependentElementIds: observedProp.dumpDependentElmtIdsObj(typeof observedProp.getUnmonitored() === 'object' ?
33        !TrackedObject.isCompatibilityMode(observedProp.getUnmonitored()) : false, isProfiler),
34      owningView: observedProp.getOwningView(),
35      length: stateMgmtDFX.getRawValueLength(observedProp),
36      syncPeers: observedProp.dumpSyncPeers(isProfiler, changedTrackPropertyName)
37    };
38  }
39
40  public static reportStateInfoToProfilerV2(target: object, attrName: string, changeIdSet: Set<number>): void {
41    const stateInfo: DumpInfo = new DumpInfo();
42    try {
43      stateMgmtDFX.HandlerStateInfoToProfilerV2(target, attrName, changeIdSet, stateInfo);
44      ViewStackProcessor.sendStateInfo(JSON.stringify(stateInfo));
45    } catch (error) {
46      stateMgmtConsole.applicationError(`An ${error.message} occurred when reporting ${target.constructor.name} information`);
47    }
48  }
49
50  private static HandlerStateInfoToProfilerV2(target: object, attrName: string, changeIdSet: Set<number>, stateInfo: DumpInfo) {
51    const decoratorInfo: string = ObserveV2.getObserve().getDecoratorInfo(target, attrName);
52    let val;
53    let id;
54    // get state value and id
55    if (Array.isArray(target) || target instanceof Set || target instanceof Map || target instanceof Date) {
56      val = target;
57      id = Utils.getArkTsUtil().getHash(target)?.toString();
58    }
59    if ((target instanceof ViewV2) || ObserveV2.IsObservedObjectV2(target)) {
60      val = Reflect.get(target, attrName);
61      id = Utils.getArkTsUtil().getHash(target)?.toString() + attrName;
62    }
63    // handle MakeObserved
64    if (target[RefInfo.MAKE_OBSERVED_PROXY]) {
65      let raw = UIUtilsImpl.instance().getTarget(target[RefInfo.MAKE_OBSERVED_PROXY]);
66      if (Array.isArray(raw) || raw instanceof Set || raw instanceof Map || raw instanceof Date ||
67        SendableType.isArray(raw) || SendableType.isMap(raw) || SendableType.isSet(raw)) {
68        val = raw;
69        id = Utils.getArkTsUtil().getHash(target)?.toString();
70      } else {
71        val = Reflect.get(raw, attrName);
72        id = Utils.getArkTsUtil().getHash(raw)?.toString() + attrName;
73      }
74    }
75
76    // dump dependent element id
77    const dependentElementIds: Array<ElementType | string> = Array<ElementType | string>();
78    changeIdSet.forEach((id: number) => {
79      if (id < ComputedV2.MIN_COMPUTED_ID) {
80        dependentElementIds.push(ObserveV2.getObserve().getElementInfoById(id, true));
81      }
82    });
83
84    // dump owned view or class
85    let ownedTarget: TargetInfo;
86    if (target instanceof ViewV2) {
87      ownedTarget = { componentName: target.constructor.name, id: target.id__() };
88    } else if (target[RefInfo.MAKE_OBSERVED_PROXY]) {
89      let raw = UIUtilsImpl.instance().getTarget(target[RefInfo.MAKE_OBSERVED_PROXY]);
90      ownedTarget = { className: raw.constructor.name, id: Utils.getArkTsUtil().getHash(raw) };
91    } else {
92      ownedTarget = { className: target.constructor.name, id: Utils.getArkTsUtil().getHash(target) };
93    }
94
95    stateInfo.observedPropertiesInfo.push({
96      decorator: decoratorInfo, propertyName: attrName, idV2: id,
97      value: stateMgmtDFX.getRawValue(val), inRenderingElementId: ObserveV2.getCurrentRecordedId(),
98      dependentElementIds: { mode: 'v2', propertyDependencies: dependentElementIds }, owningView: ownedTarget
99    });
100  }
101
102  /**
103   * Dump decorated variable for v1 and v2
104   *
105   * @param view viewPU or ViewV2
106   * @param dumpInfo contains state variable decorator, propertyName, etc.
107   */
108  public static getDecoratedVariableInfo(view: PUV2ViewBase, dumpInfo: DumpInfo): void {
109    if (view instanceof ViewV2) {
110      stateMgmtDFX.dumpV2VariableInfo(view, dumpInfo);
111    } else if (view instanceof ViewPU) {
112      stateMgmtDFX.dumpV1VariableInfo(view, dumpInfo);
113    }
114  }
115
116  private static dumpV1VariableInfo(view: ViewPU, dumpInfo: DumpInfo): void {
117    Object.getOwnPropertyNames(view)
118      .filter((varName: string) => varName.startsWith('__') && !varName.startsWith(ObserveV2.OB_PREFIX))
119      .forEach((varName) => {
120        const prop: any = Reflect.get(view, varName);
121        if (prop && typeof prop === 'object' && 'debugInfoDecorator' in prop) {
122          const observedProp: ObservedPropertyAbstractPU<any> = prop as ObservedPropertyAbstractPU<any>;
123          dumpInfo.observedPropertiesInfo.push(stateMgmtDFX.getObservedPropertyInfo(observedProp, false));
124        }
125      });
126  }
127
128  private static dumpV2VariableInfo(view: ViewV2, dumpInfo: DumpInfo): void {
129    const meta = view[ObserveV2.V2_DECO_META];
130    // no decorated variables, return view info directly
131    if (!meta) {
132      return;
133    }
134    Object.getOwnPropertyNames(meta)
135      .filter((varName) => !varName.startsWith(ProviderConsumerUtilV2.ALIAS_PREFIX)) // remove provider & consumer prefix
136      .forEach((varName) => {
137        dumpInfo.observedPropertiesInfo.push(stateMgmtDFX.dumpSingleV2VariableInfo(view, varName));
138      });
139  }
140
141  private static dumpSingleV2VariableInfo<T>(view: ViewV2, varName: string): ObservedPropertyInfo<T> {
142    const decorators: string = ObserveV2.getObserve().getDecoratorInfo(view, varName);
143    const prop: any = Reflect.get(view, varName);
144    let dependentElmIds: Set<number> | undefined = undefined;
145    if (view[ObserveV2.SYMBOL_REFS]) {
146      dependentElmIds = view[ObserveV2.SYMBOL_REFS][varName];
147    }
148
149    return {
150      decorator: decorators, propertyName: varName, id: -1, value: stateMgmtDFX.getRawValue(prop),
151      dependentElementIds:
152        { mode: 'V2', trackPropertiesDependencies: [], propertyDependencies: stateMgmtDFX.dumpDepenetElementV2(dependentElmIds) }
153      , syncPeers: []
154    };
155  }
156
157  private static dumpDepenetElementV2(dependentElmIds: Set<number> | undefined): Array<ElementType | string> {
158    const dumpElementIds: Array<ElementType | string> = [];
159    dependentElmIds?.forEach((elmtId: number) => {
160      if (elmtId < ComputedV2.MIN_COMPUTED_ID) {
161        dumpElementIds.push(ObserveV2.getObserve().getElementInfoById(elmtId));
162      } else if (elmtId < MonitorV2.MIN_WATCH_ID) {
163        dumpElementIds.push(`@Computed ${ObserveV2.getObserve().getComputedInfoById(elmtId)}`);
164      } else if (elmtId < PersistenceV2Impl.MIN_PERSISTENCE_ID) {
165        dumpElementIds.push(`@Monitor ${ObserveV2.getObserve().getMonitorInfoById(elmtId)}`);
166      } else {
167        dumpElementIds.push(`PersistenceV2[${elmtId}]`);
168      }
169    });
170    return dumpElementIds;
171  }
172
173  private static getType(item: RawValue): string {
174    try {
175      return Object.prototype.toString.call(item);
176    } catch (e) {
177      stateMgmtConsole.warn(`Cannot get the type of current value, error message is: ${e.message}`);
178      return 'unknown type';
179    }
180  }
181
182  /**
183   * Dump 10 items at most.
184   * If length > 10, the output will be the first 7 and last 3 items.
185   * eg: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
186   * output: [0, 1, 2, 3, 4, 5, 6, '...', 9, 10, 11]
187   *
188   */
189  private static dumpItems(arr: Array<RawValue>): Array<DumpBuildInType> {
190    let dumpArr = arr.slice(0, stateMgmtDFX.DUMP_MAX_LENGTH);
191    if (arr.length > stateMgmtDFX.DUMP_MAX_LENGTH) {
192      dumpArr.splice(stateMgmtDFX.DUMP_MAX_LENGTH - stateMgmtDFX.DUMP_LAST_LENGTH, stateMgmtDFX.DUMP_LAST_LENGTH, '...', ...arr.slice(-stateMgmtDFX.DUMP_LAST_LENGTH));
193    }
194    return dumpArr.map(item => (item && typeof item === 'object') ? this.getType(item) : item);
195  }
196
197  private static dumpMap(map: Map<RawValue, RawValue> | SendableMap<RawValue, RawValue>): Array<DumpBuildInType> {
198    let dumpKey = this.dumpItems(Array.from(map.keys()));
199    let dumpValue = this.dumpItems(Array.from(map.values()));
200    return dumpKey.map((item: any, index: number) => [item, dumpValue[index]]);
201  }
202
203  private static dumpObjectProperty(value: any): DumpObjectType | string {
204    let tempObj: DumpObjectType = {};
205    try {
206      let properties: string[] = Object.getOwnPropertyNames(value);
207      properties
208        .slice(0, stateMgmtDFX.DUMP_MAX_PROPERTY_COUNT)
209        .forEach((varName: string) => {
210          const propertyValue = Reflect.get(value as Object, varName);
211          tempObj[varName] = (propertyValue && typeof propertyValue === 'object') ? this.getType(propertyValue) : propertyValue;
212        });
213      if (properties.length > stateMgmtDFX.DUMP_MAX_PROPERTY_COUNT) {
214        tempObj['...'] = '...';
215      }
216    } catch (e) {
217      stateMgmtConsole.warn(`can not dump Obj, error msg ${e.message}`);
218      return 'unknown type';
219    }
220    return tempObj;
221  }
222
223  private static getRawValue<T>(prop: T | ObservedPropertyAbstractPU<T>): DumpObjectType | Array<DumpBuildInType> | T | string {
224    let rawValue: T;
225    if (prop instanceof ObservedPropertyAbstract) {
226      let wrappedValue: T = prop.getUnmonitored();
227      rawValue = ObservedObject.GetRawObject(wrappedValue);
228    } else {
229      rawValue = ObserveV2.IsProxiedObservedV2(prop) ? prop[ObserveV2.SYMBOL_PROXY_GET_TARGET] : prop;
230    }
231    if (!rawValue || typeof rawValue !== 'object') {
232      return rawValue;
233    }
234    if (rawValue instanceof Map || SendableType.isMap(rawValue as unknown as object)) {
235      return stateMgmtDFX.dumpMap(rawValue as unknown as Map<RawValue, RawValue> | SendableMap<RawValue, RawValue>);
236    } else if (rawValue instanceof Set || SendableType.isSet(rawValue as unknown as object)) {
237      return stateMgmtDFX.dumpItems(Array.from((rawValue as unknown as Set<RawValue> | SendableSet<RawValue>).values()));
238    } else if (rawValue instanceof Array || SendableType.isArray(rawValue as unknown as object)) {
239      return stateMgmtDFX.dumpItems(Array.from(rawValue as unknown as Array<RawValue>));
240    } else if (rawValue instanceof Date) {
241      return rawValue;
242    } else {
243      return stateMgmtDFX.dumpObjectProperty(rawValue);
244    }
245  }
246
247  private static getRawValueLength<T>(observedProp: ObservedPropertyAbstractPU<T>): number {
248    let wrappedValue: T = observedProp.getUnmonitored();
249    if (!wrappedValue || typeof wrappedValue !== 'object') {
250      return -1;
251    }
252    let rawObject: T = ObservedObject.GetRawObject(wrappedValue);
253    if (rawObject instanceof Map || rawObject instanceof Set) {
254      return rawObject.size;
255    } else if (rawObject instanceof Array) {
256      return rawObject.length;
257    }
258    try {
259      return Object.getOwnPropertyNames(rawObject).length;
260    } catch (e) {
261      return -1;
262    }
263  }
264}
265
266function setProfilerStatus(profilerStatus: boolean): void {
267  stateMgmtConsole.warn(`${profilerStatus ? `start` : `stop`} stateMgmt Profiler`);
268  stateMgmtDFX.enableProfiler = profilerStatus;
269}
270type PropertyDependenciesInfo = {
271  mode: string,
272  trackPropertiesDependencies?: MapItem<string, Array<ElementType | number | string>>[],
273  propertyDependencies?: Array<ElementType | string>
274}
275
276type ElementType = {
277  elementId: number,
278  elementTag: string,
279  isCustomNode: boolean,
280}
281
282// Data struct send to profiler or Inspector
283type TargetInfo = {
284  id: number, componentName?: string, className?: string, isV2?: boolean,
285  isCompFreezeAllowed_?: boolean, isViewActive_?: boolean
286};
287type ObservedPropertyInfo<T> = {
288  decorator: string, propertyName: string, value: any, id?: number, idV2?: string, inRenderingElementId?: number,
289  changedTrackPropertyName?: string | undefined,
290  dependentElementIds: PropertyDependenciesInfo,
291  length?: number
292  owningView?: TargetInfo, syncPeers?: ObservedPropertyInfo<T>[]
293};
294
295type SimpleType = string | number | boolean;
296type DumpObjectType = Record<string, SimpleType>;
297type DumpBuildInType = Array<SimpleType> | Array<[SimpleType, SimpleType]>;
298type RawValue = any;
299
300class DumpInfo {
301  public viewInfo?: TargetInfo;
302  public observedPropertiesInfo: ObservedPropertyInfo<any>[] = []
303}
304
305// global function used to throw error in Promise
306declare function _arkUIUncaughtPromiseError(error: any);
307
308function setAceDebugMode(): void {
309  stateMgmtDFX.enableDebug = true;
310}
311
312class aceDebugTrace {
313  public static begin(...args: any): void {
314    if (stateMgmtDFX.enableDebug) {
315      aceTrace.begin(...args);
316    }
317  }
318  public static end(): void {
319    if (stateMgmtDFX.enableDebug) {
320      aceTrace.end();
321    }
322  }
323}