OpenHarmony开发者论坛

标题: Ark运行时日志不打印问题分析报告 [打印本页]

作者: Laval社区小助手    时间: 2024-4-1 16:02
标题: Ark运行时日志不打印问题分析报告
[md]# 1 关键字

ark; LOG\_ECMA; 日志;

# 2 问题描述

开发板型号:rk3568

OH版本:OpenHarmony 3.1 Release

问题现象:hilog中搜不到 LOG\_ECMA 的输出(ark/runtime\_core/libpandabase/utils/logger.h)日志

测试步骤:

1. 烧镜像
2. 终端输入 `hilog<span> </span>| grep Ark`
3. eco跑一个demo

日志输出空

# 3 问题原因

## 3.1 正常机制

日志中出现如下格式的日志 ,有 Ark 函数名 行号

> ```
> 03900/Ace: [jsi_declarative_engine.cpp(PrintLog)-(0)] Ark ecmascript::GenerateProgram Line:77 GenerateProgram /data/storage/el1/bundle/com.example.entry/assets/js/MainAbility/app.abc
> ```

## 3.2 异常机制

Ark 关键字的日志为空

# 4 解决方案

通过下面方式可以看到ark的日志输出

## 方法一:

修改 `ark\runtime_core\libpandabase\utils\logger.h`文件的WriteMobileLog方法,将 `is_mlog_opened_`的判断去掉,`is_mlog_opened_`此时的值是false,在判断里面会直接return,不会执行后面的打印日志流程。

> ```
> if (mlog_buf_print == nullptr || !is_mlog_opened_) {
>     return;
> }
> ```

改为

> ```
> if (mlog_buf_print == nullptr) {
>     return;
> }
> ```

## 方法二:

注释源码里面所有的 `Logger::SetMobileLogOpenFlag(false);`

按照上面的方法可以在全量日志里看到ark相关的日志

![](https://devpress.csdnimg.cn/8d1bd0b5fba34ba9bd2344184f0f1160.png)

![](https://devpress.csdnimg.cn/d1937e4252a942069dfbf21e3ede95ed.png)

# 5 定位过程

1. 根据LOG\_ECMA查找LOG\_ECMA的定义 定义地方 `ark\js_runtime\ecmascript\ecma_macros.h`

> ```
> #define LOG_ECMA(type) \
> LOG(type, ECMASCRIPT) << __func__ << " Line:" << __LINE__ << " "  // NOLINT(bugprone-lambda-function-name)
> ```

可以得出日志是以 方法名 Line:行号的 的形式输出。

2. 再跟LOG到 `ark\runtime_core\libpandabase\utils\logger.h` 文件中 最后调用的是

> ```
> #define _LOG(level, component, p)
> panda:ogger::IsLoggingOnOrAbort(panda:ogger:evel::level, panda:ogger::Component::component) && panda:ogger::Message(panda:ogger:evel::level, panda:ogger::Component::component, p).GetStream()
> ```

3. 跟踪上步中的Message对象

```
class Message {
   public:
       Message(Level level, Component component, bool print_system_error):
       level_(level),
       component_(component),print_system_error_(print_system_error)
   {
   }

   ~Message();

   std::ostream &GetStream()
   {
       return stream_;
   }

   private:
       Level level_;
       Component component_;
       bool print_system_error_;
       std::ostringstream stream_;

       NO_COPY_SEMANTIC(Message);
       NO_MOVE_SEMANTIC(Message);
};
Logger::Message::~Message()
{
    if (print_system_error_) {
        stream_ << ": " << os::Error(errno).ToString();
    }
 
    Logger:og(level_, component_, stream_.str());
 
    if (level_ == Level::FATAL) {
        std::abort();
    }
}
```

在析构函数中看到了 `Logger:og(level_, component_, stream_.str());`

4. 跟踪 `Logger::Log`函数发现该函数中调用了 `WriteMobileLog`方法。
5. 跟踪 `WriteMobileLog`方法

```
void WriteMobileLog(Level level, const char *component, const char *message)
    {
        if (mlog_buf_print == nullptr || !is_mlog_opened_) {
            return;
    }
    PandaLog2MobileLog mlog_level = PandaLog2MobileLog::UNKNOWN;
   switch (level) {
       case Level:EBUG:
           mlog_level = PandaLog2MobileLog:EBUG;
       break;
       case Level::INFO:
           mlog_level = PandaLog2MobileLog::INFO;
       break;
       case Level::ERROR:
           mlog_level = PandaLog2MobileLog::ERROR;
       break;
       case Level::FATAL:
           mlog_level = PandaLog2MobileLog::FATAL;
       break;
       case Level::WARNING:
           mlog_level = PandaLog2MobileLog::WARN;
       break;
       default:
       UNREACHABLE();
   }
    std::string panda_component = "Ark " + std::string(component);
    mlog_buf_print(LOG_ID_MAIN, mlog_level, panda_component.c_str(), "%s", message);
}
```

* 有个判断条件 `is_mlog_opened_` 日志控制开关
* `mlog_buf_print`函数执行打印日志

6. 跟踪 `mlog_buf_print`赋值的地方

* `ark\runtime_core\libpandabase\utils\logger.h`中 `SetMobileLogPrintEntryPointByPtr`方法

```
  static void SetMobileLogPrintEntryPointByPtr(void *mlog_buf_print_ptr)
     {
         mlog_buf_print = reinterpret_cast<FUNC_MOBILE_LOG_PRINT>(mlog_buf_print_ptr);
     }
```

* `ark\js_runtime\ecmascript\napi\jsnapi.cpp`中调用 `SetMobileLogPrintEntryPointByPtr`方法的地方

```
bool JSNApi::CreateRuntime(const RuntimeOption &option)
{
   ...
 
   // Dfx
   base_options::Options baseOptions("");
   baseOptions.SetLogLevel(option.GetLogLevel());
   arg_list_t logComponents;
   logComponents.emplace_back("all");
   baseOptions.SetLogComponents(logComponents);
   Logger::Initialize(baseOptions);
   if (option.GetLogBufPrint() != nullptr) {
       Logger::SetMobileLogPrintEntryPointByPtr(reinterpret_cast<void *>(option.GetLogBufPrint()));
   }
   ...
 
   return true;
}
```

* `foundation\ace\ace_engine\frameworks\bridge\declarative_frontend\engine\jsi\ark\ark_js_runtime.cpp`中调用 `SetLogBufPrint`方法设置打印函数

```
bool ArkJSRuntime::Initialize(const std::string &libraryPath, bool isDebugMode)
{
   RuntimeOption option;
   option.SetGcType(RuntimeOption::GC_TYPE::GEN_GC);
   option.SetArkProperties(SystemProperties::GetArkProperties());
   const int64_t poolSize = 0x10000000;  // 256M
   option.SetGcPoolSize(poolSize);
   option.SetLogLevel(RuntimeOption::LOG_LEVEL::ERROR);
   option.SetLogBufPrint(print_);
   option.SetDebuggerLibraryPath(libraryPath);
   libPath_ = libraryPath;
   isDebugMode_ = isDebugMode;
 
   vm_ = JSNApi::CreateJSVM(option);
   return vm_ != nullptr;
}
```

7. 设置的打印函数

```
//文件路径
//foundation\ace\ace_engine\frameworks\bridge\declarative_frontend\engine\jsi\ark\ark_js_runtime.cpp
void ArkJSRuntime::SetLogPrint(LOG_PRINT out)
{
   print_ = out;
}
//文件路径
//foundation\ace\ace_engine\frameworks\bridge\declarative_frontend\engine\jsi\jsi_declarative_engine.cpp
bool JsiDeclarativeEngineInstance::InitJsEnv(bool debuggerMode,
const std::unordered_map<std::string, void*>& extraNativeObject, const shared_ptr<JsRuntime>& runtime)
{
   ...
 
   runtime_->SetLogPrint(PrintLog);
 
   ...
}
 
int PrintLog(int id, int level, const char* tag, const char* fmt, const char* message)
{
   switch (JsLogLevel(level - 3)) {
       case JsLogLevel::INFO:
           LOGI("%{public}s::%{public}s", tag, message);
           break;
       case JsLogLevel::WARNING:
           LOGW("%{public}s::%{public}s", tag, message);
           break;
       case JsLogLevel::ERROR:
           LOGE("%{public}s::%{public}s", tag, message);
           break;
       case JsLogLevel:EBUG:
           LOGD("%{public}s::%{public}s", tag, message);
           break;
       default:
           LOGF("%{public}s::%{public}s", tag, message);
           break;
   }
   return 0;
}
```

PrintLog中使用的打印方法是ACE子系统下的日志输出方式

8. 以上逻辑分析完,日志打印流程正常,但Ark日志没有输出,返回第5步 `WriteMobileLog`中去掉if判断条件中对 `is_mlog_opened_`的判断,测试发现有Ark相应的日志输出。故 `is_mlog_opened_`值被设置成了false。 修改方法是可以直接去掉对 `is_mlog_opened_`的判断,或者将 `is_mlog_opened_`更改为false的地方注释调 `Logger::SetMobileLogOpenFlag(false);`

# 6 知识分享

* ACE子系统通过函数指针设置ark日志的打印方法调用ark日志的打印
[/md]




欢迎光临 OpenHarmony开发者论坛 (https://forums.openharmony.cn/) Powered by Discuz! X3.5