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 */
15
16 #include "foundation/graphic/graphic_2d/utils/log/rs_trace.h"
17 #include "rs_profiler.h"
18 #include "rs_profiler_json.h"
19 #include "rs_profiler_network.h"
20
21 #include "common/rs_obj_geometry.h"
22 #include "pipeline/rs_context.h"
23 #include "pipeline/rs_display_render_node.h"
24 #include "pipeline/rs_root_render_node.h"
25 #include "pipeline/rs_surface_handler.h"
26 #include "pipeline/rs_surface_render_node.h"
27
28 namespace OHOS::Rosen {
29
DumpNode(const RSRenderNode & node,JsonWriter & out)30 void RSProfiler::DumpNode(const RSRenderNode& node, JsonWriter& out)
31 {
32 out.PushObject();
33 DumpNodeBaseInfo(node, out);
34 DumpNodeProperties(node.GetRenderProperties(), out);
35 DumpNodeOptionalFlags(node, out);
36 DumpNodeDrawCmdModifiers(node, out);
37 DumpNodeAnimations(node.animationManager_, out);
38 DumpNodeChildrenListUpdate(node, out);
39
40 auto& children = out["children"];
41 children.PushArray();
42 if (node.GetSortedChildren()) {
43 for (auto& child : *node.GetSortedChildren()) {
44 if (child) {
45 DumpNode(*child, children);
46 }
47 }
48 }
49 children.PopArray();
50 out.PopObject();
51 }
52
DumpNodeBaseInfo(const RSRenderNode & node,JsonWriter & out)53 void RSProfiler::DumpNodeBaseInfo(const RSRenderNode& node, JsonWriter& out)
54 {
55 std::string type;
56 node.DumpNodeType(node.GetType(), type);
57 out["type"] = type;
58 out["id"] = node.GetId();
59 out["instanceRootNodeId"] = node.GetInstanceRootNodeId();
60 DumpNodeSubsurfaces(node, out);
61 auto sharedTrans = node.GetSharedTransitionParam();
62 if (sharedTrans) {
63 out["SharedTransitionParam"] =
64 std::to_string(sharedTrans->inNodeId_) + " -> " + std::to_string(sharedTrans->outNodeId_);
65 }
66 if (node.IsSuggestedDrawInGroup()) {
67 out["nodeGroup"] = static_cast<int>(node.nodeGroupType_);
68 }
69 if (node.GetUifirstRootNodeId() != INVALID_NODEID) {
70 out["uifirstRootNodeId"] = node.GetUifirstRootNodeId();
71 }
72 DumpNodeSubClassNode(node, out);
73 }
74
DumpNodeSubsurfaces(const RSRenderNode & node,JsonWriter & out)75 void RSProfiler::DumpNodeSubsurfaces(const RSRenderNode& node, JsonWriter& out)
76 {
77 if (auto surface = node.ReinterpretCastTo<RSSurfaceRenderNode>(); surface && surface->HasSubSurfaceNodes()) {
78 auto& subsurface = out["subsurface"];
79 subsurface.PushArray();
80 for (auto [id, _] : surface->GetChildSubSurfaceNodes()) {
81 subsurface.Append(id);
82 }
83 subsurface.PopArray();
84 }
85 }
86
DumpNodeSubClassNode(const RSRenderNode & node,JsonWriter & out)87 void RSProfiler::DumpNodeSubClassNode(const RSRenderNode& node, JsonWriter& out)
88 {
89 auto& subclass = out["subclass"];
90 subclass.PushObject();
91 if (node.GetType() == RSRenderNodeType::SURFACE_NODE) {
92 auto& surfaceNode = static_cast<const RSSurfaceRenderNode&>(node);
93 auto p = node.parent_.lock();
94 subclass["Parent"] = p ? p->GetId() : uint64_t(0);
95 subclass["Name"] = surfaceNode.GetName();
96 out["hasConsumer"] = surfaceNode.GetRSSurfaceHandler()->HasConsumer();
97 std::string contextAlpha = std::to_string(surfaceNode.contextAlpha_);
98 std::string propertyAlpha = std::to_string(surfaceNode.GetRenderProperties().GetAlpha());
99 subclass["Alpha"] = propertyAlpha + " (include ContextAlpha: " + contextAlpha + ")";
100 subclass["Visible"] = std::to_string(surfaceNode.GetRenderProperties().GetVisible()) + " " +
101 surfaceNode.GetVisibleRegion().GetRegionInfo();
102 subclass["Opaque"] = surfaceNode.GetOpaqueRegion().GetRegionInfo();
103 subclass["OcclusionBg"] = std::to_string((surfaceNode.GetAbilityBgAlpha()));
104 subclass["SecurityLayer"] = surfaceNode.GetSecurityLayer();
105 subclass["skipLayer"] = surfaceNode.GetSkipLayer();
106 } else if (node.GetType() == RSRenderNodeType::ROOT_NODE) {
107 auto& rootNode = static_cast<const RSRootRenderNode&>(node);
108 subclass["Visible"] = rootNode.GetRenderProperties().GetVisible();
109 subclass["Size"] = { rootNode.GetRenderProperties().GetFrameWidth(),
110 rootNode.GetRenderProperties().GetFrameHeight() };
111 subclass["EnableRender"] = rootNode.GetEnableRender();
112 } else if (node.GetType() == RSRenderNodeType::DISPLAY_NODE) {
113 auto& displayNode = static_cast<const RSDisplayRenderNode&>(node);
114 subclass["skipLayer"] = displayNode.GetSecurityDisplay();
115 }
116 subclass.PopObject();
117 }
118
DumpNodeOptionalFlags(const RSRenderNode & node,JsonWriter & out)119 void RSProfiler::DumpNodeOptionalFlags(const RSRenderNode& node, JsonWriter& out)
120 {
121 if (node.GetBootAnimation()) {
122 out["GetBootAnimation"] = true;
123 }
124 if (node.isContainBootAnimation_) {
125 out["isContainBootAnimation_"] = true;
126 }
127 if (node.dirtyStatus_ != RSRenderNode::NodeDirty::CLEAN) {
128 out["isNodeDirty"] = static_cast<int>(node.dirtyStatus_);
129 }
130 if (node.GetRenderProperties().IsDirty()) {
131 out["isPropertyDirty"] = true;
132 }
133 if (node.isSubTreeDirty_) {
134 out["isSubTreeDirty"] = true;
135 }
136 if (node.IsPureContainer()) {
137 out["IsPureContainer"] = true;
138 }
139 }
140
DumpNodeDrawCmdModifiers(const RSRenderNode & node,JsonWriter & out)141 void RSProfiler::DumpNodeDrawCmdModifiers(const RSRenderNode& node, JsonWriter& out)
142 {
143 if (!node.renderContent_) {
144 return;
145 }
146
147 auto& modifiersJson = out["DrawCmdModifiers"];
148 modifiersJson.PushArray();
149 for (auto& [type, modifiers] : node.renderContent_->drawCmdModifiers_) {
150 modifiersJson.PushObject();
151 modifiersJson["type"] = static_cast<int>(type);
152 auto& modifierDesc = modifiersJson["modifiers"];
153 modifierDesc.PushArray();
154 for (const auto& modifier : modifiers) {
155 if (modifier) {
156 DumpNodeDrawCmdModifier(node, modifierDesc, static_cast<int>(type), *modifier);
157 }
158 }
159 modifiersJson.PopArray();
160 modifiersJson.PopObject();
161 }
162 modifiersJson.PopArray();
163 }
164
Hex(uint32_t value)165 static std::string Hex(uint32_t value)
166 {
167 std::stringstream sstream;
168 sstream << std::hex << value;
169 return sstream.str();
170 }
171
DumpNodeDrawCmdModifier(const RSRenderNode & node,JsonWriter & out,int type,RSRenderModifier & modifier)172 void RSProfiler::DumpNodeDrawCmdModifier(
173 const RSRenderNode& node, JsonWriter& out, int type, RSRenderModifier& modifier)
174 {
175 auto modType = static_cast<RSModifierType>(type);
176
177 if (modType < RSModifierType::ENV_FOREGROUND_COLOR) {
178 auto propertyPtr = std::static_pointer_cast<RSRenderProperty<Drawing::DrawCmdListPtr>>(modifier.GetProperty());
179 auto drawCmdListPtr = propertyPtr ? propertyPtr->Get() : nullptr;
180 auto propertyStr = drawCmdListPtr ? drawCmdListPtr->GetOpsWithDesc() : "";
181 size_t pos = 0;
182 size_t oldpos = 0;
183
184 out.PushObject();
185 auto& property = out["drawCmdList"];
186 property.PushArray();
187 while ((pos = propertyStr.find('\n', oldpos)) != std::string::npos) {
188 property.Append(propertyStr.substr(oldpos, pos - oldpos));
189 oldpos = pos + 1;
190 }
191 property.PopArray();
192 out.PopObject();
193 } else if (modType == RSModifierType::ENV_FOREGROUND_COLOR) {
194 auto propertyPtr = std::static_pointer_cast<RSRenderAnimatableProperty<Color>>(modifier.GetProperty());
195 if (propertyPtr) {
196 out.PushObject();
197 out["ENV_FOREGROUND_COLOR"] = "#" + Hex(propertyPtr->Get().AsRgbaInt()) + " (RGBA)";
198 out.PopObject();
199 }
200 } else if (modType == RSModifierType::ENV_FOREGROUND_COLOR_STRATEGY) {
201 auto propertyPtr =
202 std::static_pointer_cast<RSRenderProperty<ForegroundColorStrategyType>>(modifier.GetProperty());
203 if (propertyPtr) {
204 out.PushObject();
205 out["ENV_FOREGROUND_COLOR_STRATEGY"] = static_cast<int>(propertyPtr->Get());
206 out.PopObject();
207 }
208 } else if (modType == RSModifierType::GEOMETRYTRANS) {
209 auto propertyPtr = std::static_pointer_cast<RSRenderProperty<SkMatrix>>(modifier.GetProperty());
210 if (propertyPtr) {
211 std::string str;
212 propertyPtr->Get().dump(str, 0);
213 out.PushObject();
214 out["GEOMETRYTRANS"] = str;
215 out.PopObject();
216 }
217 }
218 }
219
DumpNodeProperties(const RSProperties & properties,JsonWriter & out)220 void RSProfiler::DumpNodeProperties(const RSProperties& properties, JsonWriter& out)
221 {
222 auto& json = out["Properties"];
223 json.PushObject();
224 json["Bounds"] = { properties.GetBoundsPositionX(), properties.GetBoundsPositionY(), properties.GetBoundsWidth(),
225 properties.GetBoundsHeight() };
226 json["Frame"] = { properties.GetFramePositionX(), properties.GetFramePositionY(), properties.GetFrameWidth(),
227 properties.GetFrameHeight() };
228
229 if (!properties.GetVisible()) {
230 json["IsVisible"] = false;
231 }
232 DumpNodePropertiesClip(properties, json);
233 DumpNodePropertiesTransform(properties, json);
234 DumpNodePropertiesDecoration(properties, json);
235 DumpNodePropertiesShadow(properties, json);
236 DumpNodePropertiesEffects(properties, json);
237 DumpNodePropertiesColor(properties, json);
238 json.PopObject();
239 }
240
DumpNodePropertiesClip(const RSProperties & properties,JsonWriter & out)241 void RSProfiler::DumpNodePropertiesClip(const RSProperties& properties, JsonWriter& out)
242 {
243 if (properties.clipToBounds_) {
244 out["ClipToBounds"] = true;
245 }
246 if (properties.clipToFrame_) {
247 out["ClipToFrame"] = true;
248 }
249 }
250
DumpNodePropertiesTransform(const RSProperties & properties,JsonWriter & out)251 void RSProfiler::DumpNodePropertiesTransform(const RSProperties& properties, JsonWriter& out)
252 {
253 if (!ROSEN_EQ(properties.GetPositionZ(), 0.f)) {
254 out["PositionZ"] = properties.GetPositionZ();
255 }
256 RSTransform defaultTransform;
257 Vector2f pivot = properties.GetPivot();
258 if ((!ROSEN_EQ(pivot[0], defaultTransform.pivotX_) || !ROSEN_EQ(pivot[1], defaultTransform.pivotY_))) {
259 out["Pivot"] = { pivot[0], pivot[1] };
260 }
261 if (!ROSEN_EQ(properties.GetRotation(), defaultTransform.rotation_)) {
262 out["Rotation"] = properties.GetRotation();
263 }
264 if (!ROSEN_EQ(properties.GetRotationX(), defaultTransform.rotationX_)) {
265 out["RotationX"] = properties.GetRotationX();
266 }
267 if (!ROSEN_EQ(properties.GetRotationY(), defaultTransform.rotationY_)) {
268 out["RotationY"] = properties.GetRotationY();
269 }
270 if (!ROSEN_EQ(properties.GetTranslateX(), defaultTransform.translateX_)) {
271 out["TranslateX"] = properties.GetTranslateX();
272 }
273 if (!ROSEN_EQ(properties.GetTranslateY(), defaultTransform.translateY_)) {
274 out["TranslateY"] = properties.GetTranslateY();
275 }
276 if (!ROSEN_EQ(properties.GetTranslateZ(), defaultTransform.translateZ_)) {
277 out["TranslateZ"] = properties.GetTranslateZ();
278 }
279 if (!ROSEN_EQ(properties.GetScaleX(), defaultTransform.scaleX_)) {
280 out["ScaleX"] = properties.GetScaleX();
281 }
282 if (!ROSEN_EQ(properties.GetScaleY(), defaultTransform.scaleY_)) {
283 out["ScaleY"] = properties.GetScaleY();
284 }
285 }
286
DumpNodePropertiesDecoration(const RSProperties & properties,JsonWriter & out)287 void RSProfiler::DumpNodePropertiesDecoration(const RSProperties& properties, JsonWriter& out)
288 {
289 if (!properties.GetCornerRadius().IsZero()) {
290 out["CornerRadius"] = { properties.GetCornerRadius().x_, properties.GetCornerRadius().y_,
291 properties.GetCornerRadius().z_, properties.GetCornerRadius().w_ };
292 }
293 if (properties.pixelStretch_.has_value()) {
294 auto& pixelStretch = out["PixelStretch"];
295 pixelStretch.PushObject();
296 pixelStretch["left"] = properties.pixelStretch_->z_;
297 pixelStretch["top"] = properties.pixelStretch_->y_;
298 pixelStretch["right"] = properties.pixelStretch_->z_;
299 pixelStretch["bottom"] = properties.pixelStretch_->w_;
300 pixelStretch.PopObject();
301 }
302 if (!ROSEN_EQ(properties.GetAlpha(), 1.f)) {
303 out["Alpha"] = properties.GetAlpha();
304 }
305 if (!ROSEN_EQ(properties.GetSpherize(), 0.f)) {
306 out["Spherize"] = properties.GetSpherize();
307 }
308
309 if (!ROSEN_EQ(properties.GetForegroundColor(), RgbPalette::Transparent())) {
310 out["ForegroundColor"] = "#" + Hex(properties.GetForegroundColor().AsArgbInt()) + " (ARGB)";
311 }
312 if (!ROSEN_EQ(properties.GetBackgroundColor(), RgbPalette::Transparent())) {
313 out["BackgroundColor"] = "#" + Hex(properties.GetBackgroundColor().AsArgbInt()) + " (ARGB)";
314 }
315 Decoration defaultDecoration;
316 if ((!ROSEN_EQ(properties.GetBgImagePositionX(), defaultDecoration.bgImageRect_.left_) ||
317 !ROSEN_EQ(properties.GetBgImagePositionY(), defaultDecoration.bgImageRect_.top_) ||
318 !ROSEN_EQ(properties.GetBgImageWidth(), defaultDecoration.bgImageRect_.width_) ||
319 !ROSEN_EQ(properties.GetBgImageHeight(), defaultDecoration.bgImageRect_.height_))) {
320 out["BgImage"] = { properties.GetBgImagePositionX(), properties.GetBgImagePositionY(),
321 properties.GetBgImageWidth(), properties.GetBgImageHeight() };
322 }
323 }
324
DumpNodePropertiesShadow(const RSProperties & properties,JsonWriter & out)325 void RSProfiler::DumpNodePropertiesShadow(const RSProperties& properties, JsonWriter& out)
326 {
327 if (!ROSEN_EQ(properties.GetShadowColor(), Color(DEFAULT_SPOT_COLOR))) {
328 out["ShadowColor"] = "#" + Hex(properties.GetShadowColor().AsArgbInt()) + " (ARGB)";
329 }
330 if (!ROSEN_EQ(properties.GetShadowOffsetX(), DEFAULT_SHADOW_OFFSET_X)) {
331 out["ShadowOffsetX"] = properties.GetShadowOffsetX();
332 }
333 if (!ROSEN_EQ(properties.GetShadowOffsetY(), DEFAULT_SHADOW_OFFSET_Y)) {
334 out["ShadowOffsetY"] = properties.GetShadowOffsetY();
335 }
336 if (!ROSEN_EQ(properties.GetShadowAlpha(), 0.f)) {
337 out["ShadowAlpha"] = properties.GetShadowAlpha();
338 }
339 if (!ROSEN_EQ(properties.GetShadowElevation(), 0.f)) {
340 out["ShadowElevation"] = properties.GetShadowElevation();
341 }
342 if (!ROSEN_EQ(properties.GetShadowRadius(), 0.f)) {
343 out["ShadowRadius"] = properties.GetShadowRadius();
344 }
345 if (!ROSEN_EQ(properties.GetShadowIsFilled(), false)) {
346 out["ShadowIsFilled"] = properties.GetShadowIsFilled();
347 }
348 }
349
DumpNodePropertiesEffects(const RSProperties & properties,JsonWriter & out)350 void RSProfiler::DumpNodePropertiesEffects(const RSProperties& properties, JsonWriter& out)
351 {
352 if (properties.border_ && properties.border_->HasBorder()) {
353 out["Border"] = properties.border_->ToString();
354 }
355 auto filter = properties.GetFilter();
356 if (filter && filter->IsValid()) {
357 out["Filter"] = filter->GetDescription();
358 }
359 auto backgroundFilter = properties.GetBackgroundFilter();
360 if (backgroundFilter && backgroundFilter->IsValid()) {
361 out["BackgroundFilter"] = backgroundFilter->GetDescription();
362 }
363 auto foregroundFilterCache = properties.GetForegroundFilterCache();
364 if (foregroundFilterCache && foregroundFilterCache->IsValid()) {
365 out["ForegroundFilter"] = foregroundFilterCache->GetDescription();
366 }
367 if (properties.outline_ && properties.outline_->HasBorder()) {
368 out["Outline"] = properties.outline_->ToString();
369 }
370 if (!ROSEN_EQ(properties.GetFrameGravity(), Gravity::DEFAULT)) {
371 out["FrameGravity"] = static_cast<int>(properties.GetFrameGravity());
372 }
373 if (properties.GetUseEffect()) {
374 out["GetUseEffect"] = true;
375 }
376 auto grayScale = properties.GetGrayScale();
377 if (grayScale.has_value() && !ROSEN_EQ(*grayScale, 0.f)) {
378 out["GrayScale"] = *grayScale;
379 }
380 if (!ROSEN_EQ(properties.GetLightUpEffect(), 1.f)) {
381 out["LightUpEffect"] = properties.GetLightUpEffect();
382 }
383 auto dynamicLightUpRate = properties.GetDynamicLightUpRate();
384 if (dynamicLightUpRate.has_value() && !ROSEN_EQ(*dynamicLightUpRate, 0.f)) {
385 out["DynamicLightUpRate"] = *dynamicLightUpRate;
386 }
387 auto dynamicLightUpDegree = properties.GetDynamicLightUpDegree();
388 if (dynamicLightUpDegree.has_value() && !ROSEN_EQ(*dynamicLightUpDegree, 0.f)) {
389 out["DynamicLightUpDegree"] = *dynamicLightUpDegree;
390 }
391 }
392
DumpNodePropertiesColor(const RSProperties & properties,JsonWriter & out)393 void RSProfiler::DumpNodePropertiesColor(const RSProperties& properties, JsonWriter& out)
394 {
395 auto brightness = properties.GetBrightness();
396 if (brightness.has_value() && !ROSEN_EQ(*brightness, 1.f)) {
397 out["Brightness"] = *brightness;
398 }
399 auto contrast = properties.GetContrast();
400 if (contrast.has_value() && !ROSEN_EQ(*contrast, 1.f)) {
401 out["Contrast"] = *contrast;
402 }
403 auto saturate = properties.GetSaturate();
404 if (saturate.has_value() && !ROSEN_EQ(*saturate, 1.f)) {
405 out["Saturate"] = *saturate;
406 }
407 auto sepia = properties.GetSepia();
408 if (sepia.has_value() && !ROSEN_EQ(*sepia, 0.f)) {
409 out["Sepia"] = *sepia;
410 }
411 auto invert = properties.GetInvert();
412 if (invert.has_value() && !ROSEN_EQ(*invert, 0.f)) {
413 out["Invert"] = *invert;
414 }
415 auto hueRotate = properties.GetHueRotate();
416 if (hueRotate.has_value() && !ROSEN_EQ(*hueRotate, 0.f)) {
417 out["HueRotate"] = *hueRotate;
418 }
419 auto colorBlend = properties.GetColorBlend();
420 if (colorBlend.has_value() && !ROSEN_EQ(*colorBlend, RgbPalette::Transparent())) {
421 out["ColorBlend"] = "#" + Hex(colorBlend->AsArgbInt()) + " (ARGB)";
422 }
423 if (!ROSEN_EQ(properties.GetColorBlendMode(), 0)) {
424 out["skblendmode"] = properties.GetColorBlendMode() - 1;
425 out["blendType"] = properties.GetColorBlendApplyType();
426 }
427 }
428
DumpNodeAnimations(const RSAnimationManager & animationManager,JsonWriter & out)429 void RSProfiler::DumpNodeAnimations(const RSAnimationManager& animationManager, JsonWriter& out)
430 {
431 if (animationManager.animations_.empty()) {
432 return;
433 }
434 auto& animations = out["RSAnimationManager"];
435 animations.PushArray();
436 for (auto [id, animation] : animationManager.animations_) {
437 if (animation) {
438 DumpNodeAnimation(*animation, animations);
439 }
440 }
441 animations.PopArray();
442 }
443
DumpNodeAnimation(const RSRenderAnimation & animation,JsonWriter & out)444 void RSProfiler::DumpNodeAnimation(const RSRenderAnimation& animation, JsonWriter& out)
445 {
446 out.PushObject();
447 out["id"] = animation.id_;
448 std::string type;
449 animation.DumpAnimationType(type);
450 out["type"] = type;
451 out["AnimationState"] = static_cast<int>(animation.state_);
452 out["StartDelay"] = animation.animationFraction_.GetDuration();
453 out["Duration"] = animation.animationFraction_.GetStartDelay();
454 out["Speed"] = animation.animationFraction_.GetSpeed();
455 out["RepeatCount"] = animation.animationFraction_.GetRepeatCount();
456 out["AutoReverse"] = animation.animationFraction_.GetAutoReverse();
457 out["Direction"] = animation.animationFraction_.GetDirection();
458 out["FillMode"] = static_cast<int>(animation.animationFraction_.GetFillMode());
459 out["RepeatCallbackEnable"] = animation.animationFraction_.GetRepeatCallbackEnable();
460 out["FrameRateRange_min"] = animation.animationFraction_.GetFrameRateRange().min_;
461 out["FrameRateRange_max"] = animation.animationFraction_.GetFrameRateRange().max_;
462 out["FrameRateRange_prefered"] = animation.animationFraction_.GetFrameRateRange().preferred_;
463 out.PopObject();
464 }
465
DumpNodeChildrenListUpdate(const RSRenderNode & node,JsonWriter & out)466 void RSProfiler::DumpNodeChildrenListUpdate(const RSRenderNode& node, JsonWriter& out)
467 {
468 if (!node.isFullChildrenListValid_) {
469 auto& childrenUpdate = out["children update"];
470 childrenUpdate.PushObject();
471 childrenUpdate["current count"] = node.fullChildrenList_->size();
472 std::string expected = std::to_string(node.GetSortedChildren()->size());
473 if (!node.disappearingChildren_.empty()) {
474 childrenUpdate["disappearing count"] = node.disappearingChildren_.size();
475 expected += " + " + std::to_string(node.disappearingChildren_.size());
476 }
477 childrenUpdate["expected count"] = expected;
478 childrenUpdate.PopObject();
479 }
480 }
481
482 } // namespace OHOS::Rosen