1 关键字

ashmem模块; foundation 崩溃;Reminder

2 问题描述

2.1 运行环境

  • 软件环境:

    • OH版本:3.1release

    • kernel版本:firefly-4.19

  • 硬件环境:

    • firefly 3568-pc

2.2 问题现象:

在使用firefly 3568-pc鸿蒙化过程中,foundation启动后会崩溃,导致后期的launcher等起不来,桌面没有显示。其中崩溃日志如下:

……
Failed to request fd from faultloggerd.
Pid:414
Uid:1000
Process name:foundation
Reason:
Signal:SIGSEGV(SEGV_MAPERR)
@0x0000001c 
Fault thread Info:
Tid:698, Name:foundation
#00 pc 00000000000034ae(00000000f55ae4ae) /system/lib/libnative_appdatafwk.z.so(_ZN4OHOS10AppDataFwk11SharedBlock11GetCellUnitEjj+5)
#01 pc 0000000000025c6c(00000000f5653c6c) /system/lib/libnative_rdb.z.so(_ZN4OHOS9NativeRdb18AbsSharedResultSet6GetIntEiRi+13)
#02 pc 00000000000fc64c(00000000f301a64c) /system/lib/libans_core.z.so(_ZN4OHOS12Notification13ReminderStore13BuildReminderERKNSt3__h10shared_ptrINS_9NativeRdb18AbsSharedResultSetEEE+173)
#03 pc 00000000000fc434(00000000f301a434) /system/lib/libans_core.z.so(_ZN4OHOS12Notification13ReminderStore12GetRemindersERKNSt3__h12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE+345)
#04 pc 00000000000fc206(00000000f301a206) /system/lib/libans_core.z.so(_ZN4OHOS12Notification13ReminderStore20GetAllValidRemindersEv+371)
#05 pc 00000000000a59ca(00000000f30cf9ca) /system/lib/libans.z.so(_ZN4OHOS12Notification19ReminderDataManager18LoadReminderFromDbEv+51)
#06 pc 00000000000a582c(00000000f30cf82c) /system/lib/libans.z.so(_ZN4OHOS12Notification19ReminderDataManager4InitEb+141)
#07 pc 00000000000a6376(00000000f30d0376) /system/lib/libans.z.so(_ZNSt3__h20__shared_ptr_emplaceIN4OHOS12Notification19ReminderDataManagerENS_9allocatorIS3_EEE16__on_zero_sharedEv+103)
#08 pc 00000000000a1d1a(00000000f30cbd1a) /system/lib/libans.z.so(_ZN4OHOS12Notification19ReminderDataManager12InitInstanceERKNS_4sptrINS0_27AdvancedNotificationServiceEEE+67)
#09 pc 00000000000707b2(00000000f309a7b2) /system/lib/libans.z.so(_ZN4OHOS12Notification34AdvancedNotificationServiceAbility7OnStartEv+103)
#10 pc 0000000000011c88(00000000f7be8c88) /system/lib/libsystem_ability_fwk.z.so(_ZN4OHOS13SystemAbility5StartEv+105)
#11 pc 000000000000e3ba(00000000f7be53ba) /system/lib/libsystem_ability_fwk.z.so(_ZN4OHOS19LocalAbilityManager22StartSystemAbilityTaskEPNS_13SystemAbilityE+411)
#12 pc 000000000001d07a(00000000f7bb607a) /system/lib/libutils.z.so(_ZN4OHOS10ThreadPool12WorkInThreadEv+55)
#13 pc 000000000001d44e(00000000f7bb644e) /system/lib/libutils.z.so(_ZNSt3__h12__deque_baseINS_8functionIFvvEEENS_9allocatorIS3_EEE5clearEv+235)
#14 pc 00000000000bdb54(00000000f7cb7b54) /system/lib/ld-musl-arm.so.1(pthread_create+2184)
#15 pc 0000000000057f00(00000000f7c51f00) /system/lib/ld-musl-arm.so.1(__aeabi_read_tp+256)
……

ashmem错误日志:

AshmemOpenLocked: fd is invalid, fd = -1

2.3 测试步骤

  • 1、生成firefly 4.19 未修改过的源码内核文件。boot.img,MiniLoaderAll.bin,misc.img,parameter.txt,recovery.img,uboot.img

  • 2、生成OpenHarmony 3.1-Release 编译后提供的system.img,userdata.img,vector.img

  • 3、使用步骤1提供的分区表文件parameter.txt,把img下载到硬件。

  • 4、使用hilog查看打印信息,会发现崩溃日志出现。

3 问题原因

3.1 正常机制

在foundation::Reminder初始化过程中,会读取数据库。其中会使用dev/ashmem 创建匿名共享内存。然后通过共享内存解析详细的数据。完成后foundation继续初始化其它模块。直到launcher启动。

3.2 异常机制

在3.1 正常机制使用dev/ashmem创建匿名共享内存中,/dev/ashmem文件找不到,导致创建共享内存失败,句柄返回为空。而后面数据解析过程中,又引用了空指针,导致崩溃。foundation无法完成后续的初始化,launcher无法启动。

4 解决方案

foundation崩溃是内核缺少/dev/ashmem模块。在内核编译中添加下如编译配置项解决。

CONFIG_ASHMEM=y

5 定位过程

5.1 异常日志的获取

按2.3测试步骤 说明获取hilog日志,通过搜索Signal:SIGSEGV,可以快速得到crash的日志。所有与crash相关的日志都可以通过此方法快速筛选。

5.2 异常日志的解析

根据问题现象中日志我们可以得到以下信息:

Process name:foundation ->进程名:foundation

Reason: Signal:SIGSEGV(SEGV_MAPERR)->崩溃类型:内存操作错误

libnative_appdatafwk.z.so(_ZN4OHOS10AppDataFwk11SharedBlock11GetCellUnitEjj+5)

最顶层的函数名:AppDataFwk::SharedBlock::GetCellUnit。so库名:native_appdatafwk。详细的解析方法,请参考6.2 c++符号解析。

详细的崩溃堆栈如下:

  • SharedBlock ::GetCellUnit (崩溃点)

    • AbsSharedResultSet::GetInt(int columnIndex, int &value)

      • reminder = BuildReminder(queryResultSet);

        • GetReminders(queryCondition);

          • store_->GetAllValidReminders();

          • void ReminderDataManager::LoadReminderFromDb()

          • void ReminderDataManager::Init(bool isFromBootComplete)

5.3 堆栈代码解析

分析代码后总结如下:

在nottifaction的Reminider有读取数据库,LoadReminderFromDb。数据库相关代码如下:

std::vector<sptr<ReminderRequest>> ReminderStore::GetAllValidReminders()
{
    //创建sql语句
    std::string queryCondition = "select * from " + REMINDER_DB_TABLE + " where "
        + ReminderRequest::IS_EXPIRED + " is false order by "
        + ReminderRequest::TRIGGER_TIME + " asc";
    ANSR_LOGD("Get all reminders");
    return GetReminders(queryCondition);
}
​
std::vector<sptr<ReminderRequest>> ReminderStore::GetReminders(const std::string &queryCondition)
{
    std::vector<sptr<ReminderRequest>> reminders;
    if (rdbStore_ == nullptr) {
        ANSR_LOGE("Rdb store is not initialized.");
        return reminders;
    }
    //查询数据库信息
    std::shared_ptr<NativeRdb::AbsSharedResultSet> queryResultSet = Query(queryCondition);
    if (queryResultSet == nullptr) {
        return reminders;
    }
    bool isAtLastRow = false;  
    queryResultSet->IsAtLastRow(isAtLastRow);
    //解析数据库
    while (!isAtLastRow) {
        ANSR_LOGE("Lex in!");
        queryResultSet->GoToNextRow();
        sptr<ReminderRequest> reminder;
        reminder = BuildReminder(queryResultSet);//解析过程崩溃
        reminders.push_back(reminder);
        queryResultSet->IsAtLastRow(isAtLastRow);
    }
    ANSR_LOGD("Size=%{public}d", reminders.size());
    return reminders;
}

整个数据库操作崩溃过程分3步:

1、创建数据库语句。//没有问题

2、读取数据库。//返回正常

3、解析崩溃。//与正常对比,有差异。正常是没有数据的,但崩溃确有数据返回

然后通过hdc把数据库文件导出。文件位置:

# pwd
/data/system_ce/ans_standard
# ls
reminder.db  reminder.db-shm  reminder.db-wal 

导出文件后,与正常文件大小没有区别,也没有任何数据。

可以推断出第3步没有问题。那在数据库没有数据的情况下,第二步确读出了数据,应该是第二步数据库查询出现了问题。数据库查询代码如下:

//查询
std::shared_ptr<NativeRdb::AbsSharedResultSet> ReminderStore::Query(const std::string &queryCondition) const
{
    std::unique_ptr<NativeRdb::AbsSharedResultSet> queryResultSet;
    if (rdbStore_ == nullptr) {
        ANSR_LOGE("Rdb store is not initialized.");
        return queryResultSet;
    }
    std::vector<std::string> whereArgs;
    queryResultSet = rdbStore_->QuerySql(queryCondition, whereArgs);
    return queryResultSet;
}
//构建SqliteSharedResultSet类用于查询
std::unique_ptr<AbsSharedResultSet> RdbStoreImpl::QuerySql(const std::string &sql,
    const std::vector<std::string> &selectionArgs)
{
    RDB_TRACE_BEGIN("rdb query sql");
    auto resultSet = std::make_unique<SqliteSharedResultSet>(shared_from_this(), path, sql, selectionArgs);
    RDB_TRACE_END();
    return resultSet;
}

数据库查询,是构建了SqliteSharedResultSet 这个类,以下是类的构建方法:

//SqliteSharedResultSet 类的声明
class SqliteSharedResultSet : public AbsSharedResultSet {
public:
    SqliteSharedResultSet(std::shared_ptr<RdbStoreImpl> rdbSreImpl, std::string path, std::string sql,
        const std::vector<std::string> &selectionArgVec);
​
//类的构建
SqliteSharedResultSet::SqliteSharedResultSet(std::shared_ptr<RdbStoreImpl> rdbSreImpl, std::string path,
    std::string sql, const std::vector<std::string> &bindArgs)
    : AbsSharedResultSet(path), resultSetBlockCapacity(0), isOnlyFillResultSetBlock(false), rdbStoreImpl(rdbSreImpl),
      qrySql(sql), selectionArgVec(bindArgs), rowNum(NO_COUNT)
{}
​
//有使用共享内存
AbsSharedResultSet::AbsSharedResultSet(std::string name)
{
    AppDataFwk::SharedBlock::Create(name, DEFAULT_BLOCK_SIZE, sharedBlock_);
}
​
//使用ashmem机制共享内存
int SharedBlock::Create(const std::string &name, size_t size, SharedBlock *&outSharedBlock)
​
sptr<Ashmem> ashmem = Ashmem::CreateAshmem(ashmemName.c_str(), size);
    
int fd = AshmemCreate(name, size);
    
int fd = AshmemOpen();
​
static int AshmemOpenLocked()
//使用内核设备文件/dev/ashmem 
if (fd < 0) {
        fd = TEMP_FAILURE_RETRY(open("/dev/ashmem", O_RDWR | O_CLOEXEC));
}
//找不到这个设备文件
if (fd < 0) {
    UTILS_LOGE("%{public}s: fd is invalid, fd = %{public}d", __func__, fd);
    return fd;
}

从以上的层层代码可以看出,最终是找不到/dev/ashmem这个文件描述。所以这个共享内存创建失败了,导致sharedBlock_=nullptr。但在数据库解析中(SharedBlock ::GetCellUnit (崩溃点))又用到的了这个共享内存,代码如下:

int AbsSharedResultSet::GetInt(int columnIndex, int &value)
{
    //引用了空指针,导致崩溃
    AppDataFwk::SharedBlock::CellUnit *cellUnit = sharedBlock_->GetCellUnit(rowPos, columnIndex);
    if (!cellUnit) {
        LOG_ERROR("AbsSharedResultSet::GetInt cellUnit is null!");
        return E_ERROR;
    }
    value = (int)cellUnit->cell.longValue;
    return E_OK;
}

 

6 知识分享

6.1 ashmem

ashmem:匿名共享内存的实现是以Ashmem驱动程序为基础所构建起来的一套方案,基于linux的临时文件系统tmpfs。依赖驱动文件dev/ashmem。在OpenHarmony中用于进程间的内存共享。

6.2 c++符号解析

GCC的基本C++名称修饰方法如下:所有的符号都以"_Z"开头,对于嵌套的名字(在名称空间或在类里面的),后面紧跟"N",然后是各个名称空间和类的名字,每个名字前是名字字符串长度,再以"E"结尾。

以本文的第一条崩溃日志为例,解析如下:
libnative_appdatafwk.z.so(_ZN4OHOS10AppDataFwk11SharedBlock11GetCellUnitEjj+5)
libnative_appdatafwk.z.so:代表的是native_appdatafwk模块
_Z 开头标识
N 代表后面信息是在名称空间或在类里面。
4 OHOS 长度
OHOS 命令空间
10 代表 AppDataFwk的长度
AppDataFwk 第一层类名
11 代表SharedBlock长度
SharedBlock 第二层类名
11 代表GetCellUnit长度
GetCellUnit 最后代码函数名
E 代表结束,如果是函数,后紧跟参数类型
最后解析出的信息是:
OHOS::AppDataFwk::SharedBlock::GetCellUnit(j,j)
Logo

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

更多推荐