1.源码:Openharmony-4.1-realease,测试手机“类似开发者7885”.

2.背景:在执行ActsAceEtsComponentUITest时,用例未完全执行完成,就退出了;查看后台发现有crash文件产生;

3.崩溃用例:apiCommponentAddJsunit_1900,该用例是讲一个图片传递给底层进行模糊化处理,并显示;

4.崩溃截图,有多样,主要集中在:

void SkiaShaderEffect::InitWithImage(

    const Image& image, TileMode tileX, TileMode tileY, const SamplingOptions& sampling, const Matrix& matrix)函数中,在构建SkiaMatrix和SkiaImage时发生崩溃;

#00 pc 00000000000ce7bc /system/lib64/platformsdk/lib2d_graphics.z.so(OHOS::Rosen::Drawing::Matrix::PreScale(float, float)+0)(dc995f73c3aae248e5ea435bf1896015)
#01 pc 00000000000f17c4 /system/lib64/platformsdk/lib2d_graphics.z.so(OHOS::Rosen::Drawing::SkiaShaderEffect::InitWithImage(OHOS::Rosen::Drawing::Image const&, OHOS::Rosen::Drawing::TileMode, OHOS::Rosen::Drawing::TileMode, OHOS::Rosen::Drawing::SamplingOptions const&, OHOS::Rosen::Drawing::Matrix const&)+132)(dc995f73c3aae248e5ea435bf1896015)
#02 pc 00000000000d4148 /system/lib64/platformsdk/lib2d_graphics.z.so(OHOS::Rosen::Drawing::ShaderEffect::CreateImageShader(OHOS::Rosen::Drawing::Image const&, OHOS::Rosen::Drawing::TileMode, OHOS::Rosen::Drawing::TileMode, OHOS::Rosen::Drawing::SamplingOptions const&, OHOS::Rosen::Drawing::Matrix const&)+112)(dc995f73c3aae248e5ea435bf1896015)
#03 pc 0000000000300058 /system/lib64/platformsdk/librender_service_base.z.so(OHOS::Rosen::KawaseBlurFilter::ApplyKawaseBlur(OHOS::Rosen::Drawing::Canvas&, std::__h::shared_ptr<OHOS::Rosen::Drawing::Image> const&, OHOS::Rosen::KawaseParameter const&)+3424)(c803ca53397def2a41491aeb0050511b)
#04 pc 00000000003085f4 /system/lib64/platformsdk/librender_service_base.z.so(OHOS::Rosen::RSMaterialFilter::DrawImageRect(OHOS::Rosen::Drawing::Canvas&, std::__h::shared_ptr<OHOS::Rosen::Drawing::Image> const&, OHOS::Rosen::Drawing::RectF const&, OHOS::Rosen::Drawing::RectF const&) const+628)(c803ca53397def2a41491aeb0050511b)
#05 pc 00000000002d0154 /system/lib64/platformsdk/librender_service_base.z.so(OHOS::Rosen::RSPropertiesPainter::DrawFilter(OHOS::Rosen::RSProperties const&, OHOS::Rosen::RSPaintFilterCanvas&, OHOS::Rosen::FilterType, std::__h::optional<OHOS::Rosen::Drawing::RectF> const&, std::__h::shared_ptr<OHOS::Rosen::RSFilter> const&)+2096)(c803ca53397def2a41491aeb0050511b)
#06 pc 0000000000276210 /system/lib64/platformsdk/librender_service_base.z.so(OHOS::Rosen::RSCanvasRenderNode::ProcessAnimatePropertyAfterChildren(OHOS::Rosen::RSPaintFilterCanvas&)+304)(c803ca53397def2a41491aeb0050511b)
#07 pc 00000000000f7f64 /system/lib64/platformsdk/librender_service_client.z.so(OHOS::Rosen::RSRenderThreadVisitor::ProcessCanvasRenderNode(OHOS::Rosen::RSCanvasRenderNode&)+1220)(8e7e5ed9ccc5463f9d95f3963d87992e)
#08 pc 0000000000275830 /system/lib64/platformsdk/librender_service_base.z.so(OHOS::Rosen::RSCanvasRenderNode::Process(std::__h::shared_ptr<OHOS::Rosen::RSNodeVisitor> const&)+124)(c803ca53397def2a41491aeb0050511b)
#09 pc 00000000000f6508 /system/lib64/platformsdk/librender_service_client.z.so(OHOS::Rosen::RSRenderThreadVisitor::ProcessChildren(OHOS::Rosen::RSRenderNode&)+532)(8e7e5ed9ccc5463f9d95f3963d87992e)
#10 pc 00000000000f7c94 /system/lib64/platformsdk/librender_service_client.z.so(OHOS::Rosen::RSRenderThreadVisitor::ProcessCanvasRenderNode(OHOS::Rosen::RSCanvasRenderNode&)+500)(8e7e5ed9ccc5463f9d95f3963d87992e)
#11 pc 0000000000275830 /system/lib64/platformsdk/librender_service_base.z.so(OHOS::Rosen::RSCanvasRenderNode::Process(std::__h::shared_ptr<OHOS::Rosen::RSNodeVisitor> const&)+124)(c803ca53397def2a41491aeb0050511b)
#12 pc 00000000000f6508 /system/lib64/platformsdk/librender_service_client.z.so(OHOS::Rosen::RSRenderThreadVisitor::ProcessChildren(OHOS::Rosen::RSRenderNode&)+532)(8e7e5ed9ccc5463f9d95f3963d87992e)
#13 pc 00000000000f7c94 /system/lib64/platformsdk/librender_service_client.z.so(OHOS::Rosen::RSRenderThreadVisitor::ProcessCanvasRenderNode(OHOS::Rosen::RSCanvasRenderNode&)+500)(8e7e5ed9ccc5463f9d95f3963d87992e)
#14 pc 0000000000275830 /system/lib64/platformsdk/librender_service_base.z.so(OHOS::Rosen::RSCanvasRenderNode::Process(std::__h::shared_ptr<OHOS::Rosen::RSNodeVisitor> const&)+124)(c803ca53397def2a41491aeb0050511b)
#15 pc 00000000000f6508 /system/lib64/platformsdk/librender_service_client.z.so(OHOS::Rosen::RSRenderThreadVisitor::ProcessChildren(OHOS::Rosen::RSRenderNode&)+532)(8e7e5ed9ccc5463f9d95f3963d87992e)
#16 pc 00000000000f7c94 /system/lib64/platformsdk/librender_service_client.z.so(OHOS::Rosen::RSRenderThreadVisitor::ProcessCanvasRenderNode(OHOS::Rosen::RSCanvasRenderNode&)+500)(8e7e5ed9ccc5463f9d95f3963d87992e)
#17 pc 0000000000275830 /system/lib64/platformsdk/librender_service_base.z.so(OHOS::Rosen::RSCanvasRenderNode::Process(std::__h::shared_ptr<OHOS::Rosen::RSNodeVisitor> const&)+124)(c803ca53397def2a41491aeb0050511b)
#18 pc 00000000000f6508 /system/lib64/platformsdk/librender_service_client.z.so(OHOS::Rosen::RSRenderThreadVisitor::ProcessChildren(OHOS::Rosen::RSRenderNode&)+532)(8e7e5ed9ccc5463f9d95f3963d87992e)
#19 pc 00000000000f7c94 /system/lib64/platformsdk/librender_service_client.z.so(OHOS::Rosen::RSRenderThreadVisitor::ProcessCanvasRenderNode(OHOS::Rosen::RSCanvasRenderNode&)+500)(8e7e5ed9ccc5463f9d95f3963d87992e)
#20 pc 00000000000f6f8c /system/lib64/platformsdk/librender_service_client.z.so(OHOS::Rosen::RSRenderThreadVisitor::ProcessRootRenderNode(OHOS::Rosen::RSRootRenderNode&)+2496)(8e7e5ed9ccc5463f9d95f3963d87992e)
#21 pc 00000000000f6508 /system/lib64/platformsdk/librender_service_client.z.so(OHOS::Rosen::RSRenderThreadVisitor::ProcessChildren(OHOS::Rosen::RSRenderNode&)+532)(8e7e5ed9ccc5463f9d95f3963d87992e)
#22 pc 00000000000f0f34 /system/lib64/platformsdk/librender_service_client.z.so(std::__h::__function::__func<OHOS::Rosen::RSRenderThread::RSRenderThread()::$_1, std::__h::allocator<OHOS::Rosen::RSRenderThread::RSRenderThread()::$_1>, void ()>::operator()() (.ab551116fc081835199bc1917a091c1f)+3836)(8e7e5ed9ccc5463f9d95f3963d87992e)
#23 pc 00000000000ef508 /system/lib64/platformsdk/librender_service_client.z.so(OHOS::Rosen::RSRenderThread::OnVsync(unsigned long)+268)(8e7e5ed9ccc5463f9d95f3963d87992e)
#24 pc 00000000000197f8 /system/lib64/platformsdk/libvsync.z.so(OHOS::Rosen::VSyncCallBackListener::OnReadable(int)+1084)(32f50fbb67348444139bd9c51f4e7b19)
#25 pc 0000000000013b50 /system/lib64/chipset-pub-sdk/libeventhandler.z.so(std::__h::__function::__func<OHOS::AppExecFwk::EventQueue::HandleFileDescriptorEvent(int, unsigned int, std::__h::basic_string<char, std::__h::char_traits<char>, std::__h::allocator<char>> const&)::$_9, std::__h::allocator<OHOS::AppExecFwk::EventQueue::HandleFileDescriptorEvent(int, unsigned int, std::__h::basic_string<char, std::__h::char_traits<char>, std::__h::allocator<char>> const&)::$_9>, void ()>::operator()()+220)(e56bdab4aef1a423d28b19e1e152efba)
#26 pc 0000000000011a70 /system/lib64/chipset-pub-sdk/libeventhandler.z.so(OHOS::AppExecFwk::EventHandler::DistributeEvent(std::__h::unique_ptr<OHOS::AppExecFwk::InnerEvent, void (*)(OHOS::AppExecFwk::InnerEvent*)> const&)+1044)(e56bdab4aef1a423d28b19e1e152efba)

 

修改过程:

1.尝试在InitWithImage函数中对传入的image做非空等判断,发现崩溃依旧存在;matrix做类似的操作也出现类似的问题;

2.溯源,找到InitWithImage参数的生成函数,

#ifndef USE_ROSEN_DRAWING
bool KawaseBlurFilter::ApplyKawaseBlur(SkCanvas& canvas, const sk_sp<SkImage>& image, const KawaseParameter& param)
#else
bool KawaseBlurFilter::ApplyKawaseBlur(Drawing::Canvas& canvas, const std::shared_ptr<Drawing::Image>& image,
    const KawaseParameter& param)
#endif

该函数是InitWithImage参数的参数生成函数;

3.结合崩溃日志,锁定崩溃行数:

prebuilts/clang/ohos/linux-x86_64/llvm/bin/llvm-addr2line -Cfie out/oriole/lib.unstripped/graphic/graphic_2d/librender_service_base.z.so 0000000000300058(crash文件中的崩溃地址)


blurBuilder.SetChild("imageInput", Drawing::ShaderEffect::CreateImageShader(*tmpBlur, Drawing::TileMode::CLAMP,
            Drawing::TileMode::CLAMP, linear, Drawing::Matrix()));这段代码传递的参数有问题;

 

4.原因分析:

原因 1:tmpBlur 为 nullptr,解引用触发空指针崩溃(最可能)
tmpBlur 是通过 blurBuilder.MakeImage(...) 创建的,但代码中未对 tmpBlur 做空指针检查:
MakeImage 依赖 GPU 上下文(canvas.GetGPUContext().get()),若 GPU 上下文无效(如未初始化、已释放),MakeImage 会返回 nullptr。
循环中每次都会覆盖 tmpBlur,若某次 MakeImage 失败,tmpBlur 会变为 nullptr,后续执行 *tmpBlur 解引用时,直接触发空指针崩溃。
即使首次 MakeImage 成功,后续循环中也可能因 GPU 资源不足、Shader 配置错误等导致 tmpBlur 为 nullptr。
原因 2:默认构造的 Drawing::Matrix 未正确初始化,内部指针为空
代码中传入 CreateImageShader 的是默认构造的 Drawing::Matrix():
cpp
运行
Drawing::ShaderEffect::CreateImageShader(*tmpBlur, Drawing::TileMode::CLAMP,
    Drawing::TileMode::CLAMP, linear, Drawing::Matrix())
若 Drawing::Matrix 的默认构造函数未初始化内部的 Skia 矩阵实例(如 SkiaMatrix 指针为 nullptr),在 SkiaShaderEffect::InitWithImage 中操作该矩阵(如调用 PreScale)时,会因访问空指针崩溃。
从堆栈第 00 帧 Matrix::PreScale 崩溃可印证:PreScale 内部可能直接操作未初始化的底层矩阵指针。
原因 3:tmpBlur 内部的 SkiaImage 实例生命周期异常
虽 tmpBlur 是 std::shared_ptr<Image>,但可能存在:
MakeImage 创建的 Image 内部 SkiaImage 实例未正确初始化(如 skiaImage_ 为 nullptr)。
多线程并发导致 tmpBlur 的引用计数异常,内部 SkiaImage 被提前释放,解引用 *tmpBlur 时访问悬空指针。

经过分析比较认同原因1;这个与测试手机的硬件相关,本手机部分硬件重新适配了,可能在某些地方存在未知问题,例如渲染未使用GPU等;原因2,该函数是通用函数,出错的概率较低;原因3:skia基础实现是三方库,ApplyKawaseBlur函数是基于三方库的实现,其实现思路或逻辑未知,因此,其生命周期不好判断。

在基于原因1的基础上将ApplyKawaseBlur实现做了以下修改;经测试,ActsAceEtsComponentUITest模块能执行完整,未出现crash文件。

#ifndef USE_ROSEN_DRAWING
bool KawaseBlurFilter::ApplyKawaseBlur(SkCanvas& canvas, const sk_sp<SkImage>& image, const KawaseParameter& param)
#else
bool KawaseBlurFilter::ApplyKawaseBlur(Drawing::Canvas& canvas, const std::shared_ptr<Drawing::Image>& image,
    const KawaseParameter& param)
#endif
{
    if (!blurEffect_ || !mixEffect_ || !image) {
        ROSEN_LOGE("KawaseBlurFilter::shader error, use Gauss instead");
        return false;
    }
    static auto useKawaseOriginal = RSSystemProperties::GetKawaseOriginalEnabled();
    if (param.radius <= 0 || useKawaseOriginal) {
        ROSEN_LOGD("KawaseBlurFilter::input invalid radius : %{public}d", param.radius);
        OutputOriginalImage(canvas, image, param);
        return true;
    }
    auto src = param.src;
    auto dst = param.dst;
    auto input = image;
    CheckInputImage(canvas, image, param, input);
    ComputeRadiusAndScale(param.radius);
    RS_OPTIONAL_TRACE_BEGIN("ApplyKawaseBlur " + GetDescription());
    int maxPasses = supportLargeRadius ? kMaxPassesLargeRadius : kMaxPasses;
    float dilatedConvolutionFactor = supportLargeRadius ? kDilatedConvolutionLargeRadius : kDilatedConvolution;
    if (abs(dilatedConvolutionFactor) <= 1e-6) {
        return false;
    }
    float tmpRadius = static_cast<float>(blurRadius_) / dilatedConvolutionFactor;
    int numberOfPasses = std::min(maxPasses, std::max(static_cast<int>(ceil(tmpRadius)), 1)); // 1 : min pass num
    float radiusByPasses = tmpRadius / numberOfPasses;
    ROSEN_LOGD("KawaseBlurFilter::kawase radius : %{public}f, scale : %{public}f, pass num : %{public}d",
        blurRadius_, blurScale_, numberOfPasses);
#ifndef USE_ROSEN_DRAWING
    auto width = std::max(static_cast<int>(std::ceil(dst.width())), input->width());
    auto height = std::max(static_cast<int>(std::ceil(dst.height())), input->height());
    SkImageInfo scaledInfo = input->imageInfo().makeWH(std::ceil(width * blurScale_), std::ceil(height * blurScale_));
    SkMatrix blurMatrix = SkMatrix::Translate(-src.fLeft, -src.fTop);
    blurMatrix.postScale(blurScale_, blurScale_);
    SkSamplingOptions linear(SkFilterMode::kLinear, SkMipmapMode::kNone);

    // Advanced Filter: check is AF usable only the first time
    bool isUsingAF = IS_ADVANCED_FILTER_USABLE_CHECK_ONCE && blurEffectAF_ != nullptr;
    SkRuntimeShaderBuilder blurBuilder(isUsingAF ? blurEffectAF_ : blurEffect_);
    blurBuilder.child("imageInput") = input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix);

    if (isUsingAF) {
        SkV2 firstPassOffsets[BLUR_SAMPLE_COUNT];
        OffsetInfo firstPassOffsetInfo = {radiusByPasses * blurScale_, radiusByPasses * blurScale_,
            scaledInfo.width(), scaledInfo.height()};
        getNormalizedOffset(firstPassOffsets, BLUR_SAMPLE_COUNT, firstPassOffsetInfo);
        blurBuilder.uniform("in_blurOffset") = firstPassOffsets;
    } else {
        blurBuilder.uniform("in_blurOffset") = SkV2{radiusByPasses * blurScale_, radiusByPasses * blurScale_};
        blurBuilder.uniform("in_maxSizeXY") = SkV2{width * blurScale_, height * blurScale_};
    }

    sk_sp<SkImage> tmpBlur(blurBuilder.makeImage(canvas.recordingContext(), nullptr, scaledInfo, false));
    // And now we'll build our chain of scaled blur stages
    for (auto i = 1; i < numberOfPasses; i++) {
        const float stepScale = static_cast<float>(i) * blurScale_;
        blurBuilder.child("imageInput") = tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);

        // Advanced Filter
        if (isUsingAF) {
            SkV2 offsets[BLUR_SAMPLE_COUNT];
            OffsetInfo offsetInfo = {radiusByPasses * stepScale, radiusByPasses * stepScale,
                scaledInfo.width(), scaledInfo.height()};
            getNormalizedOffset(offsets, BLUR_SAMPLE_COUNT, offsetInfo);
            blurBuilder.uniform("in_blurOffset") = offsets;
        } else {
            blurBuilder.uniform("in_blurOffset") = SkV2{radiusByPasses * stepScale, radiusByPasses * stepScale};
            blurBuilder.uniform("in_maxSizeXY") = SkV2{width * blurScale_, height * blurScale_};
        }
        tmpBlur = blurBuilder.makeImage(canvas.recordingContext(), nullptr, scaledInfo, false);
    }
#else
    auto width = std::max(static_cast<int>(std::ceil(dst.GetWidth())), input->GetWidth());
    auto height = std::max(static_cast<int>(std::ceil(dst.GetHeight())), input->GetHeight());
    auto originImageInfo = input->GetImageInfo();
    auto scaledInfo = Drawing::ImageInfo(std::ceil(width * blurScale_), std::ceil(height * blurScale_),
        originImageInfo.GetColorType(), originImageInfo.GetAlphaType(), originImageInfo.GetColorSpace());
    Drawing::Matrix blurMatrix;
    blurMatrix.Translate(-src.GetLeft(), -src.GetTop());
    blurMatrix.PostScale(blurScale_, blurScale_);
    Drawing::SamplingOptions linear(Drawing::FilterMode::LINEAR, Drawing::MipmapMode::NONE);

    // Advanced Filter: check is AF usable only the first time
    bool isUsingAF = IS_ADVANCED_FILTER_USABLE_CHECK_ONCE && blurEffectAF_ != nullptr;
    Drawing::RuntimeShaderBuilder blurBuilder(isUsingAF ? blurEffectAF_ : blurEffect_);
    blurBuilder.SetChild("imageInput", Drawing::ShaderEffect::CreateImageShader(*input, Drawing::TileMode::CLAMP,
        Drawing::TileMode::CLAMP, linear, blurMatrix));

    if (isUsingAF) {
        SkV2 firstPassOffsets[BLUR_SAMPLE_COUNT];
        OffsetInfo firstPassOffsetInfo = {radiusByPasses * blurScale_, radiusByPasses * blurScale_,
            scaledInfo.GetWidth(), scaledInfo.GetHeight()};
        getNormalizedOffset(firstPassOffsets, BLUR_SAMPLE_COUNT, firstPassOffsetInfo);
        blurBuilder.SetUniform("in_blurOffset", firstPassOffsetInfo.offsetX, firstPassOffsetInfo.offsetY,
            firstPassOffsetInfo.width, firstPassOffsetInfo.height);
    } else {
        blurBuilder.SetUniform("in_blurOffset", radiusByPasses * blurScale_, radiusByPasses * blurScale_);
        blurBuilder.SetUniform("in_maxSizeXY", width * blurScale_, height * blurScale_);
    }

    // 先获取并检查GPU上下文,再创建tmpBlur
    auto gpuContext = canvas.GetGPUContext().get();
    if (!gpuContext) {
        ROSEN_LOGE("KawaseBlurFilter::ApplyKawaseBlur: GPU context is null");
        OutputOriginalImage(canvas, image, param);
        return true;
    }

    // 首次创建tmpBlur,使用已验证的gpuContext
    std::shared_ptr<Drawing::Image> tmpBlur = blurBuilder.MakeImage(gpuContext, nullptr, scaledInfo, false);
    // 检查tmpBlur是否创建成功
    if (!tmpBlur) {
        ROSEN_LOGE("KawaseBlurFilter::ApplyKawaseBlur: first MakeImage failed");
        OutputOriginalImage(canvas, image, param);
        return true;
    }    

    // And now we'll build our chain of scaled blur stages
    for (auto i = 1; i < numberOfPasses; i++) {
        const float stepScale = static_cast<float>(i) * blurScale_;
        // 重新检查GPU上下文(防止中途失效)
        gpuContext = canvas.GetGPUContext().get();
        if (!gpuContext) {
            ROSEN_LOGE("KawaseBlurFilter::ApplyKawaseBlur: GPU context invalid in loop");
            OutputOriginalImage(canvas, image, param);
            return true;
        }

        // 检查tmpBlur是否有效(防止上一轮创建失败)
        if (!tmpBlur) {
            ROSEN_LOGE("KawaseBlurFilter::ApplyKawaseBlur: tmpBlur is null in loop");
            OutputOriginalImage(canvas, image, param);
            return true;
        }

        blurBuilder.SetChild("imageInput", Drawing::ShaderEffect::CreateImageShader(*tmpBlur, Drawing::TileMode::CLAMP,
            Drawing::TileMode::CLAMP, linear, Drawing::Matrix()));

        // Advanced Filter
        if (isUsingAF) {
            SkV2 offsets[BLUR_SAMPLE_COUNT];
            OffsetInfo offsetInfo = {radiusByPasses * stepScale, radiusByPasses * stepScale,
                scaledInfo.GetWidth(), scaledInfo.GetHeight()};
            getNormalizedOffset(offsets, BLUR_SAMPLE_COUNT, offsetInfo);
            blurBuilder.SetUniform("in_blurOffset", offsetInfo.offsetX, offsetInfo.offsetY, offsetInfo.width,
                offsetInfo.height);
        } else {
            blurBuilder.SetUniform("in_blurOffset", radiusByPasses * stepScale, radiusByPasses * stepScale);
            blurBuilder.SetUniform("in_maxSizeXY", width * blurScale_, height * blurScale_);
        }
        tmpBlur = blurBuilder.MakeImage(canvas.GetGPUContext().get(), nullptr, scaledInfo, false);
    }
#endif
    RS_OPTIONAL_TRACE_END();
    return ApplyBlur(canvas, input, tmpBlur, param);
}

 

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐