ashmem 导致foundation crash问题分析报告
1 关键字 ashmem模块; foundation 崩溃;Reminder 2 问题描述 2.1 运行环境 软件环境: OH版本:3.1release kernel版本:firefly-4.19 硬件环境: firefly 3568-pc 2.2 问题现象: 在使用firefly 3568-pc鸿蒙化过程中,foundation启动后会崩溃,导致后期的launcher等起不来,桌面没有显示。其
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)
更多推荐
所有评论(0)