• 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): void {
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 propertyVariableNames: [string, any][] = view.__getDecoratorPropertyName__V2View__Internal();
130    // no decorated variables, return view info directly
131    if (propertyVariableNames.length === 0) {
132      return;
133    }
134    propertyVariableNames
135      .filter((entry) => !entry[0].startsWith(ProviderConsumerUtilV2.ALIAS_PREFIX))
136      .forEach((entry) => {
137        dumpInfo.observedPropertiesInfo.push(stateMgmtDFX.dumpSingleV2VariableInfo(view, entry));
138      });
139  }
140
141  private static dumpSingleV2VariableInfo<T>(view: ViewV2, entry: [string, any]): ObservedPropertyInfo<T> {
142    const varName = entry[0];
143    const deco: any = entry[1];
144    const decorators: string = ObserveV2.getObserve().parseDecorator(deco);
145    const prop: any = Reflect.get(view, varName);
146    let dependentElmIds: Set<number> | undefined = undefined;
147    if (view[ObserveV2.SYMBOL_REFS]) {
148      dependentElmIds = view[ObserveV2.SYMBOL_REFS][varName];
149    }
150
151    return {
152      decorator: decorators, propertyName: varName, id: -1, value: stateMgmtDFX.getRawValue(prop),
153      dependentElementIds:
154        { mode: 'V2', trackPropertiesDependencies: [], propertyDependencies: stateMgmtDFX.dumpDepenetElementV2(dependentElmIds) }
155      , syncPeers: []
156    };
157  }
158
159  private static dumpDepenetElementV2(dependentElmIds: Set<number> | undefined): Array<ElementType | string> {
160    const dumpElementIds: Array<ElementType | string> = [];
161    dependentElmIds?.forEach((elmtId: number) => {
162      if (elmtId < ComputedV2.MIN_COMPUTED_ID) {
163        dumpElementIds.push(ObserveV2.getObserve().getElementInfoById(elmtId));
164      } else if (elmtId < MonitorV2.MIN_WATCH_ID) {
165        dumpElementIds.push(`@Computed ${ObserveV2.getObserve().getComputedInfoById(elmtId)}`);
166      } else if (elmtId < PersistenceV2Impl.MIN_PERSISTENCE_ID) {
167        dumpElementIds.push(`@Monitor ${ObserveV2.getObserve().getMonitorInfoById(elmtId)}`);
168      } else {
169        dumpElementIds.push(`PersistenceV2[${elmtId}]`);
170      }
171    });
172    return dumpElementIds;
173  }
174
175  private static getType(item: RawValue): string {
176    try {
177      return Object.prototype.toString.call(item);
178    } catch (e) {
179      stateMgmtConsole.warn(`Cannot get the type of current value, error message is: ${e.message}`);
180      return 'unknown type';
181    }
182  }
183
184  /**
185   * Dump 10 items at most.
186   * If length > 10, the output will be the first 7 and last 3 items.
187   * eg: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
188   * output: [0, 1, 2, 3, 4, 5, 6, '...', 9, 10, 11]
189   *
190   */
191  private static dumpItems(arr: Array<RawValue>): Array<DumpBuildInType> {
192    let dumpArr = arr.slice(0, stateMgmtDFX.DUMP_MAX_LENGTH);
193    if (arr.length > stateMgmtDFX.DUMP_MAX_LENGTH) {
194      dumpArr.splice(stateMgmtDFX.DUMP_MAX_LENGTH - stateMgmtDFX.DUMP_LAST_LENGTH, stateMgmtDFX.DUMP_LAST_LENGTH, '...', ...arr.slice(-stateMgmtDFX.DUMP_LAST_LENGTH));
195    }
196    return dumpArr.map(item => (item && typeof item === 'object') ? this.getType(item) : item);
197  }
198
199  private static dumpMap(map: Map<RawValue, RawValue> | SendableMap<RawValue, RawValue>): Array<DumpBuildInType> {
200    let dumpKey = this.dumpItems(Array.from(map.keys()));
201    let dumpValue = this.dumpItems(Array.from(map.values()));
202    return dumpKey.map((item: any, index: number) => [item, dumpValue[index]]);
203  }
204
205  private static dumpObjectProperty(value: any): DumpObjectType | string {
206    let tempObj: DumpObjectType = {};
207    try {
208      let properties: string[] = Object.getOwnPropertyNames(value);
209      properties
210        .slice(0, stateMgmtDFX.DUMP_MAX_PROPERTY_COUNT)
211        .forEach((varName: string) => {
212          const propertyValue = Reflect.get(value as Object, varName);
213          tempObj[varName] = (propertyValue && typeof propertyValue === 'object') ? this.getType(propertyValue) : propertyValue;
214        });
215      if (properties.length > stateMgmtDFX.DUMP_MAX_PROPERTY_COUNT) {
216        tempObj['...'] = '...';
217      }
218    } catch (e) {
219      stateMgmtConsole.warn(`can not dump Obj, error msg ${e.message}`);
220      return 'unknown type';
221    }
222    return tempObj;
223  }
224
225  private static getRawValue<T>(prop: T | ObservedPropertyAbstractPU<T>): DumpObjectType | Array<DumpBuildInType> | T | string {
226    let rawValue: T;
227    if (prop instanceof ObservedPropertyAbstract) {
228      let wrappedValue: T = prop.getUnmonitored();
229      rawValue = ObservedObject.GetRawObject(wrappedValue);
230    } else {
231      rawValue = ObserveV2.IsProxiedObservedV2(prop) ? prop[ObserveV2.SYMBOL_PROXY_GET_TARGET] : prop;
232    }
233    if (!rawValue || typeof rawValue !== 'object') {
234      return rawValue;
235    }
236    if (rawValue instanceof Map || SendableType.isMap(rawValue as unknown as object)) {
237      return stateMgmtDFX.dumpMap(rawValue as unknown as Map<RawValue, RawValue> | SendableMap<RawValue, RawValue>);
238    } else if (rawValue instanceof Set || SendableType.isSet(rawValue as unknown as object)) {
239      return stateMgmtDFX.dumpItems(Array.from((rawValue as unknown as Set<RawValue> | SendableSet<RawValue>).values()));
240    } else if (rawValue instanceof Array || SendableType.isArray(rawValue as unknown as object)) {
241      return stateMgmtDFX.dumpItems(Array.from(rawValue as unknown as Array<RawValue>));
242    } else if (rawValue instanceof Date) {
243      return rawValue;
244    } else {
245      return stateMgmtDFX.dumpObjectProperty(rawValue);
246    }
247  }
248
249  private static getRawValueLength<T>(observedProp: ObservedPropertyAbstractPU<T>): number {
250    let wrappedValue: T = observedProp.getUnmonitored();
251    if (!wrappedValue || typeof wrappedValue !== 'object') {
252      return -1;
253    }
254    let rawObject: T = ObservedObject.GetRawObject(wrappedValue);
255    if (rawObject instanceof Map || rawObject instanceof Set) {
256      return rawObject.size;
257    } else if (rawObject instanceof Array) {
258      return rawObject.length;
259    }
260    try {
261      return Object.getOwnPropertyNames(rawObject).length;
262    } catch (e) {
263      return -1;
264    }
265  }
266}
267
268function setProfilerStatus(profilerStatus: boolean): void {
269  stateMgmtConsole.warn(`${profilerStatus ? `start` : `stop`} stateMgmt Profiler`);
270  stateMgmtDFX.enableProfiler = profilerStatus;
271}
272type PropertyDependenciesInfo = {
273  mode: string,
274  trackPropertiesDependencies?: MapItem<string, Array<ElementType | number | string>>[],
275  propertyDependencies?: Array<ElementType | string>
276}
277
278type ElementType = {
279  elementId: number,
280  elementTag: string,
281  isCustomNode: boolean,
282}
283
284// Data struct send to profiler or Inspector
285type TargetInfo = {
286  id: number, componentName?: string, className?: string, isV2?: boolean,
287  isCompFreezeAllowed_?: boolean, isViewActive_?: boolean
288};
289type ObservedPropertyInfo<T> = {
290  decorator: string, propertyName: string, value: any, id?: number, idV2?: string, inRenderingElementId?: number,
291  changedTrackPropertyName?: string | undefined,
292  dependentElementIds: PropertyDependenciesInfo,
293  length?: number
294  owningView?: TargetInfo, syncPeers?: ObservedPropertyInfo<T>[]
295};
296
297type SimpleType = string | number | boolean;
298type DumpObjectType = Record<string, SimpleType>;
299type DumpBuildInType = Array<SimpleType> | Array<[SimpleType, SimpleType]>;
300type RawValue = any;
301
302class DumpInfo {
303  public viewInfo?: TargetInfo;
304  public observedPropertiesInfo: ObservedPropertyInfo<any>[] = []
305}
306
307// global function used to throw error in Promise
308declare function _arkUIUncaughtPromiseError(error: any);
309
310function setAceDebugMode(): void {
311  stateMgmtDFX.enableDebug = true;
312}
313
314class aceDebugTrace {
315  public static begin(...args: any): void {
316    if (stateMgmtDFX.enableDebug) {
317      aceTrace.begin(...args);
318    }
319  }
320  public static end(): void {
321    if (stateMgmtDFX.enableDebug) {
322      aceTrace.end();
323    }
324  }
325}