背景介绍
应用启动性能是Android设备上一个比较重要的性能指标。该方案通过预读取文件的方式,实现了提升应用启动性能的目的。该方案后面简称IO-prefectch。
方案流程图
触发一次IO-prefectch流程的调用流程如下:
iop_server工作线程处理Msg流程如下:
预读取流程如下:
源码分析
frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
void findTaskToMoveToFront(TaskRecord task, int flags, ActivityOptions options, String reason,
boolean forceNonResizeable) {
//top_activity = task.stack.topRunningActivityLocked();
/* App is launching from recent apps and it's a new process */
if(top_activity != null && top_activity.getState() == ActivityState.DESTROYED) {
acquireAppLaunchPerfLock(top_activity);
}
}
ActivityRecord findTaskLocked(ActivityRecord r, int displayId) {
mTmpFindTaskResult.r = null;
mTmpFindTaskResult.matchedByRootAffinity = false;
ActivityRecord affinityMatch = null;
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r);
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = display.getChildAt(stackNdx);
if (!r.hasCompatibleActivityType(stack)) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping stack: (mismatch activity/stack) "
+ stack);
continue;
}
stack.findTaskLocked(r, mTmpFindTaskResult);
// It is possible to have tasks in multiple stacks with the same root affinity, so
// we should keep looking after finding an affinity match to see if there is a
// better match in another stack. Also, task affinity isn't a good enough reason
// to target a display which isn't the source of the intent, so skip any affinity
// matches not on the specified display.
if (mTmpFindTaskResult.r != null) {
if (!mTmpFindTaskResult.matchedByRootAffinity) {
if(mTmpFindTaskResult.r.getState() == ActivityState.DESTROYED ) {
/*It's a new app launch */
acquireAppLaunchPerfLock(r);
}
if(mTmpFindTaskResult.r.getState() == ActivityState.STOPPED) {
/*Warm launch */
acquireUxPerfLock(BoostFramework.UXE_EVENT_SUB_LAUNCH, r.packageName);
}
return mTmpFindTaskResult.r;
} else if (mTmpFindTaskResult.r.getDisplayId() == displayId) {
// Note: since the traversing through the stacks is top down, the floating
// tasks should always have lower priority than any affinity-matching tasks
// in the fullscreen stacks
affinityMatch = mTmpFindTaskResult.r;
} else if (DEBUG_TASKS && mTmpFindTaskResult.matchedByRootAffinity) {
Slog.d(TAG_TASKS, "Skipping match on different display "
+ mTmpFindTaskResult.r.getDisplayId() + " " + displayId);
}
}
}
}
/* Acquire perf lock *only* during new app launch */
if (mTmpFindTaskResult.r == null || mTmpFindTaskResult.r.getState() == ActivityState.DESTROYED) {
acquireAppLaunchPerfLock(r);
}
if (DEBUG_TASKS && affinityMatch == null) Slog.d(TAG_TASKS, "No task found");
return affinityMatch;
}
void acquireAppLaunchPerfLock(ActivityRecord r) {
/* Acquire perf lock during new app launch */
if (mPerfBoost == null) {
mPerfBoost = new BoostFramework();
}
if (mPerfBoost != null) {
mPerfBoost.perfHint(BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, r.packageName, -1, BoostFramework.Launch.BOOST_V1);
mPerfSendTapHint = true;
mPerfBoost.perfHint(BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, r.packageName, -1, BoostFramework.Launch.BOOST_V2);
if(mPerfBoost.perfGetFeedback(BoostFramework.VENDOR_FEEDBACK_WORKLOAD_TYPE, r.packageName) == BoostFramework.WorkloadType.GAME)
{
mPerfHandle = mPerfBoost.perfHint(BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, r.packageName, -1,
BoostFramework.Launch.BOOST_GAME);
} else {
mPerfHandle = mPerfBoost.perfHint(BoostFramework.VENDOR_HINT_FIRST_LAUNCH_BOOST, r.packageName, -1,
BoostFramework.Launch.BOOST_V3);
}
if (mPerfHandle > 0)
mIsPerfBoostAcquired = true;
// Start IOP
Slog.d(TAG, "perfIOPrefetchStart");
mPerfBoost.perfIOPrefetchStart(-1,r.packageName,
r.appInfo.sourceDir.substring(0, r.appInfo.sourceDir.lastIndexOf('/')));
}
}
frameworks/base/core/java/android/util/BoostFramework.java
/** @hide */
public int perfIOPrefetchStart(int pid, String pkgName, String codePath) {
int ret = -1;
try {
Object retVal = sIOPStart.invoke(mPerf, pid, pkgName, codePath);
ret = (int) retVal;
Log.d(TAG, "ret1 = " + ret);
} catch (Exception e) {
Log.e(TAG, "Exception " + e);
}
try {
Object retVal = sUxIOPStart.invoke(mUxPerf, pid, pkgName, codePath);
ret = (int) retVal;
Log.d(TAG, "ret2 = " + ret);
} catch (Exception e) {
Log.e(TAG, "Ux Perf Exception " + e);
}
return ret;
}
vendor/qcom/proprietary/commonsys/android-perf/UxPerfomance/src/com/qualcomm/qti/UxPerformance.java
public int perfIOPrefetchStart(int PId, String PkgName, String CodePath)
{
if(EnablePrefetch)
{
QLogI("DexPrefetchThread Feature is Enable ");
PreDexThread = new DexPrefetchThread(CodePath);
if(PreDexThread != null)
(new Thread(PreDexThread)).start();
}
else
{
QLogI("DexPrefetchThread Feature is disabled ");
return REQUEST_FAILED;
}
return REQUEST_SUCCEEDED;
}
private class DexPrefetchThread implements Runnable {
public String CodePath;
public DexPrefetchThread(String CodePath) {
this.CodePath = CodePath;
}
public void LoadFiles(String path,String FileName)
{
String[] Files = {".art", ".odex", ".vdex"};
QLogD(" LoadFiles() path is " + path);
for ( int i = 0; i< Files.length; i++ )
{
File MyFile = new File(path + FileName + Files[i]);
if(MyFile.exists())
{
try {
FileChannel fileChannel = new RandomAccessFile(MyFile, "r").getChannel();
MappedByteBuffer buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size());
if(EnableDebug)
QLogD( "Before Is Buffer Loaded " + buffer.isLoaded());
QLogD( "File is " + path + FileName + Files[i]);
buffer.load();
if(EnableDebug)
QLogD( "After Is Buffer Loaded " + buffer.isLoaded());
} catch (FileNotFoundException e) {
QLogE( "DexPrefetchThread Can not find file " + FileName + Files[i] + "at " + path);
} catch (IOException e) {
QLogE( "DexPrefetchThread IO Error file " + FileName + Files[i] + "at " + path);
}
}
}
}
public void run() {
//Fix me : This loop is added just to comply with thread run functions.
while(true) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "DexPrefetchThread");
if ( CodePath.startsWith("/data") )
{
//Check for 32/64 bit apps
QLogD( "Data pkg ");
if((new File(CodePath + "/oat/arm64/")).exists())
{
LoadFiles(CodePath + "/oat/arm64/", "base");
}
else if((new File(CodePath + "/oat/arm/")).exists())
{
LoadFiles(CodePath + "/oat/arm/", "base");
}
}
else
{
// Get the name of package
QLogD( "system/vendor pkg ");
String[] SplitPath = CodePath.split("/");
String PkgName = SplitPath[SplitPath.length-1];
QLogD( "PKgNAme : " + PkgName);
//Check for 32/64 bit apps
if((new File(CodePath + "/oat/arm64/")).exists())
{
LoadFiles(CodePath + "/oat/arm64/", PkgName);
}
else if((new File(CodePath + "/oat/arm/")).exists())
{
LoadFiles(CodePath + "/oat/arm/", PkgName);
}
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
return;
}
}
}
vendor/qcom/proprietary/commonsys/android-perf/QPerformance/src/com/qualcomm/qti/Performance.java
public int perfIOPrefetchStart(int PId, String Pkg_name, String Code_path)
{
return native_perf_io_prefetch_start(PId,Pkg_name, Code_path);
}
vendor/qcom/proprietary/android-perf/iop-hal/Iop.cpp
Return<int32_t> Iop::iopStart(int32_t pid, const hidl_string& pkg_name, const hidl_string& code_path) {
iop_msg_t pMsg;
uint32_t retVal = -1;
memset(&pMsg, 0, sizeof(iop_msg_t));
pMsg.cmd = IOP_CMD_PERFLOCK_IOPREFETCH_START;
pMsg.pid = pid;
strlcpy(pMsg.pkg_name, pkg_name.c_str(), PKG_LEN);
strlcpy(pMsg.code_path, code_path.c_str(), PKG_LEN);
if (mHandle.dlhandle != NULL)
retVal = (*(mHandle.iop_server_submit_request))(&pMsg);
return retVal;
}
vendor/qcom/proprietary/android-perf/io-p/io-p.cpp
static void *iop_server(void *data)
{
for (;;) {
switch (cmd) {
case IOP_CMD_PERFLOCK_IOPREFETCH_START:
{
static bool is_in_recent_list = false;
char property[PROPERTY_VALUE_MAX];
int enable_prefetcher = 0;
int enable_prefetcher_ofr = 0;
property_get("vendor.enable_prefetch", property, "0");
enable_prefetcher = strtod(property, NULL);
property_get("vendor.iop.enable_prefetch_ofr", property, "0");
enable_prefetcher_ofr = strtod(property, NULL);
// if PID < 0 consider it as playback operation
if(msg->pid < 0)
{
int ittr = 0;
char *week_day = NULL;
week_day = (char *) malloc(6*sizeof(char));
// Insert package into the table
if (week_day == NULL) {
//Malloc failed. Most-probably low on memory.
break;
}
strlcpy(pkg_info.pkg_name,msg->pkg_name,PKG_NAME_LEN);
strlcpy(tmp_pkg_name,pkg_info.pkg_name,PKG_NAME_LEN);
bindApp_dur = 0;
disAct_dur = 0;
launching = true;
time(&pkg_info.last_time_launched);
compute_time_day_slot(week_day, &time_slot);
QLOGI("UXEngine Updating Details: pkg_name: %s, week_day: %s, time_slot: %d %s\n", pkg_info.pkg_name, week_day, time_slot, tmp_pkg_name);
update_ux_pkg_details(pkg_info, week_day, time_slot, 0);
update_palm_table(msg->pkg_name, 0, 1);
QLOGI("UXEngine finished ux_pkg_details update \n");
free(week_day);
is_in_recent_list = false;
if(!enable_prefetcher)
{
QLOGE("io prefetch is disabled");
break;
}
//Check app is in recent list
for(ittr = 0; ittr < IOP_NO_RECENT_APP; ittr++)
{
if(0 == strcmp(msg->pkg_name,recent_list[ittr]))
{
is_in_recent_list = true;
QLOGE("is_in_recent_list is TRUE");
break;
}
}
// IF Application is in recent list, return
if(true == is_in_recent_list)
{
QLOGE("io prefetch is deactivate");
break;
}
if(recent_list_cnt == IOP_NO_RECENT_APP)
recent_list_cnt = 0;
//Copy the package name to recent list
strlcpy(recent_list[recent_list_cnt],msg->pkg_name,PKG_LEN);
recent_list_cnt++;
stop_capture();
stop_playback();
start_playback(msg->pkg_name);
}
// if PID > 0 then consider as capture operation
if(msg->pid > 0)
{
if(!enable_prefetcher)
{
QLOGE("io prefetch is disabled");
break;
}
if(true == is_in_recent_list)
{
QLOGE("io prefetch Capture is deactivated ");
break;
}
stop_capture();
start_capture(msg->pid,msg->pkg_name,msg->code_path,enable_prefetcher_ofr);
}
break;
}
}
}
}
vendor/qcom/proprietary/android-perf/io-p/io-prefetch/list_capture.cpp
/*
* start capture function, this function will start new thead to get list of the files
*/
int start_capture(pid_t pid, char *pkg_name, char *code_path, bool ofr) {
char property[PROPERTY_VALUE_MAX];
capture_thread_arg *arg_bundle = NULL;
int len = 0;
int len_code_path = 0;
if(NULL == pkg_name)
return -1;
len = strlen(pkg_name);
if ( len <= 0)
{
QLOGW("incorrect length for pkg_name");
return -1;
}
arg_bundle = (capture_thread_arg *)malloc(sizeof(capture_thread_arg));
if (NULL == arg_bundle)
return -1;
total_files = 0;
QLOGE(" start_capture ENTER ");
arg_bundle->pid = pid;
arg_bundle->pkg_name = (char *) malloc(len+1);
arg_bundle->ofr = ofr;
len_code_path = strlen(code_path);
if (NULL == arg_bundle->pkg_name) {
free(arg_bundle);
return -1;
}
QLOGI("%d %s %zd %d\n", pid, arg_bundle->pkg_name,strlen(pkg_name),PKG_NAME_LEN);
strlcpy(arg_bundle->pkg_name, pkg_name, len+1);
strlcpy(list_code_path, code_path, len_code_path+1);
QLOGI("%d %s %zd %s %zd\n", pid, arg_bundle->pkg_name,strlen(pkg_name),
list_code_path, strlen(code_path));
halt_thread = 0;
start_capture_time = (long) now_ms();
time(&start_capture_time_s);
property_get("vendor.iop.read_fd_interval_ms", property, "50");
read_fd_interval_ms = atoi(property);
if(read_fd_interval_ms <= 0) {
QLOGI("read_fd_interval_ms is set to a non-positive value %d. Resetting to default value 50\n", read_fd_interval_ms);
property_set("read_fd_interval_ms", "50");
read_fd_interval_ms = 50;
}
QLOGI("property = %d\n", read_fd_interval_ms);
//TBD must remove comments
if(pthread_create(&capture_pthread_id, NULL, capture_thread, arg_bundle)) {
return -1;
}
QLOGI("start_capture EXIT");
return 0;
}
void * capture_thread(void * arg) {
int i = 0;
int pkg_len = 0;
int duration_counter = 0;
pkg_details pkg_info;
int index = 0;
int halt_counter = CAPTURE_MAX_DURATION/read_fd_interval_ms;
int polling_enable = 0;
char property[PROPERTY_VALUE_MAX];
property_get("vendor.polling_enable", property, "0");
polling_enable = atoi(property);
ATRACE_BEGIN("capture_thread");
capture_thread_arg * arg_bundle = (capture_thread_arg *)arg;
//Copy the package to same string to remove ':'
pkg_len = strlen(arg_bundle->pkg_name);
i = 0;
while(i < pkg_len && arg_bundle->pkg_name[i] != ':')
{
i++;
}
arg_bundle->pkg_name[i] = '\0';
strlcpy(list_pkg_name,arg_bundle->pkg_name,PKG_NAME_LEN);
QLOGI("pkg_name = %s",arg_bundle->pkg_name);
if(polling_enable)
{
while (duration_counter < halt_counter) {
if (halt_thread) goto cleanup;
QLOGI("Getting snapshot %d\n", arg_bundle->pid);
get_snapshot(list_pkg_name,arg_bundle->pid);
duration_counter++;
usleep(read_fd_interval_ms * 1000);
}
}
get_priv_code_files(arg_bundle->pkg_name);
get_priv_files(arg_bundle);
QLOGI("pkg_name = %s total_files = %d ",arg_bundle->pkg_name,total_files);
// Insert package into the table
strlcpy(pkg_info.pkg_name,arg_bundle->pkg_name,PKG_NAME_LEN);
time(&pkg_info.last_time_launched);
// Update Mincore data
if(arg_bundle->ofr)
{
for(index = 0; index < total_files;index++)
{
if(update_mincore_data(file_list[index]) != 0)
{
file_list[index]->mincore_array = NULL;
}
}
}
update_pkg_details(pkg_info);
update_file_details(arg_bundle->pkg_name, file_list, total_files);
delete_mark_files();
cleanup:
//log a report about how many files need insert or update this time
QLOGE("# Final entry : pkg_name file_name file_time_stamp filesize file_iop_size");
for(i = 0; i < total_files; i++) {
QLOGE("%d. Final entry : %s %s %d %d %d\n",i
,arg_bundle->pkg_name,file_list[i]->file_name
, file_list[i]->file_time_stamp, file_list[i]->filesize, file_list[i]->file_iop_size);
if (file_list[i]->mincore_array) {
free(file_list[i]->mincore_array);
file_list[i]->mincore_array = NULL;
}
free(file_list[i]);
}
ATRACE_END();
free(arg_bundle->pkg_name);
free(arg_bundle);
QLOGI("Exit capture_thread");
return NULL;
}
int get_snapshot(char *pkg_name, pid_t pid)
{
struct stat sb_link, sb;
char dirname[PATH_MAX + 1];
char pathname[PATH_MAX + 1];
char linkname[PATH_MAX + 1];
ssize_t r;
int need_to_filter = 0;
DIR *dp;
struct dirent *ep;
long file_capture_time;
char trace_buf[1024];
snprintf(trace_buf, sizeof(trace_buf), "GetSnapSthot");
ATRACE_BEGIN(trace_buf);
snprintf(dirname,PATH_MAX,"/proc/%d/fd", pid);
QLOGI("Dirname = %s MAX_PATH = %d\n", dirname, PATH_MAX);
dp = opendir(dirname);
QLOGI("get_snapshot Enter dp = %p",dp);
QLOGI("dp = %p",dp);
if (dp != NULL) {
int i = 0;
while ((ep = readdir(dp)))
{
snprintf(pathname, PATH_MAX , "%s/%s", dirname, ep->d_name);
int ret = lstat(pathname, &sb_link);
if (ret == -1) {
continue;
}
file_capture_time = (long) now_ms() - start_capture_time;
r = readlink(pathname, linkname, PATH_MAX + 1);
if (r < 0) {
continue;
}
linkname[r] = '\0';
need_to_filter = file_need_to_filter(linkname);
if(need_to_filter)
{
need_to_filter = 0;
QLOGI("file is in filter list %s",linkname);
continue; // goto next file
}
if (stat(linkname, &sb) == -1) {
continue;
}
if(memcmp("/storage/emulated/0/",linkname,20) == 0) {
for(i = r; i >= 19; i--) {
linkname[i+5] = linkname[i];
}
memcpy((void *)&linkname[18],(void *)"legacy", 6);
}
if (stat(linkname, &sb) == -1) {
continue;
}
if (S_ISREG(sb.st_mode)) {
find_insert_into_list(pkg_name, linkname, sb.st_size, file_capture_time);
}
}
QLOGI("get_snapshot Exit dp = %p",dp);
closedir(dp);
}
ATRACE_END();
return 0;
}
vendor/qcom/proprietary/android-perf/io-p/io-prefetch/file_playback.cpp
void start_playback(char *pkg_name_arg) {
char *pkg_name = NULL;
int len = 0;
QLOGE(" start_playback-1 ");
if(NULL == pkg_name_arg)
return;
len = strlen(pkg_name_arg);
if(len <= 0)
{
QLOGW("Incorrect length for pkg_name_arg");
return;
}
pkg_name = (char *) malloc(len + 1);
if (NULL == pkg_name)
return;
strlcpy(pkg_name, pkg_name_arg,len+1);
QLOGI(" %s %zd\n", pkg_name,strlen(pkg_name));
if(pthread_create(&start_playback_pthread_id, NULL, start_playback_thread, pkg_name)) {
return ;
}
QLOGI("Exit playback");
return ;
}
static void* start_playback_thread(void *pkg_name_arg)
{
file_details *file_detail_ptr;
int num_file = 0;
int num_of_files = 0;
char *pkg_name = (char *)pkg_name_arg;
int i = 0;
total_data = 0;
struct stat file_stat;
int pagesize = sysconf(_SC_PAGESIZE);
ATRACE_BEGIN("start_playback_thread: Enter");
QLOGI("pkg_name = %s",pkg_name);
num_file = get_total_file(pkg_name);
if( num_file <= 0)
{
QLOGI("no file to read get_total_file %d", num_file);
remove_pkg(pkg_name);
free(pkg_name);
ATRACE_END();
return NULL;
}
file_detail_ptr = (file_details *) malloc(sizeof(file_details) * num_file);
if (NULL == file_detail_ptr)
{
QLOGI("Fail to allocate memory");
remove_pkg(pkg_name);
free(pkg_name);
ATRACE_END();
return NULL;
}
num_of_files = get_file_list(pkg_name,file_detail_ptr,num_file);
QLOGI("num_of_files = %d",num_of_files);
if (num_of_files <= 0)
{
QLOGI("no file to read get_file_list");
remove_pkg(pkg_name);
free(file_detail_ptr);
free(pkg_name);
ATRACE_END();
return NULL;
}
if( num_file > MAX_NUM_FILE)
{
//free mincore array that exceed MAX_NUM_FILE
for (i=MAX_NUM_FILE; i<num_of_files; i++) {
if (file_detail_ptr[i].mincore_array)
{
free(file_detail_ptr[i].mincore_array);
file_detail_ptr[i].mincore_array=NULL;
}
}
num_file = MAX_NUM_FILE;
}
for(i = 0; i < num_file;i++)
{
int fd = -1;
char trace_buf[4096];
if (file_detail_ptr[i].disabled) {
QLOGE("skip preload disabled file %d %s", i, file_detail_ptr[i].file_name);
continue;
}
if (file_detail_ptr[i].study_finish && file_detail_ptr[i].file_iop_size < pagesize) {
file_detail_ptr[i].cache_dropped = NEED_MARK_CACHE_NOT_DROPPED;
QLOGE("skip preload ZERO needed file %d %s", i, file_detail_ptr[i].file_name);
continue;
}
fd = open(file_detail_ptr[i].file_name, O_RDONLY);
if(fd == -1)
{
mark_for_delete(pkg_name,file_detail_ptr[i].file_name);
continue;
}
if ( fstat( fd, &file_stat ) < 0 )
{
QLOGI("fail to get file stat");
mark_for_delete(pkg_name,file_detail_ptr[i].file_name);
close(fd);
continue;// goto next file
}
if ( file_stat.st_size == 0 )
{
// File zie is zero
mark_for_delete(pkg_name,file_detail_ptr[i].file_name);
close(fd);
continue;// goto next file
}
snprintf(trace_buf,4096,"Opening file %s size = %d",file_detail_ptr[i].file_name
, (int)file_stat.st_size);
ATRACE_BEGIN(trace_buf);
if (file_stat.st_mtime != file_detail_ptr[i].file_modify_time) {
if (strstr(file_detail_ptr[i].file_name,".apk")!=NULL
&& (strstr(file_detail_ptr[i].file_name,"/data/app/")!=NULL ||
strstr(file_detail_ptr[i].file_name, "/system/app/")!=NULL ||
strstr(file_detail_ptr[i].file_name, "/system/priv-app/")!=NULL))
{
//readonly /data/app/base.apk
QLOGI("apk file %s modify time changed %d/%ld, \
remove files belong to pkg, and restudy",
file_detail_ptr[i].file_name,
file_detail_ptr[i].file_modify_time, file_stat.st_mtime);
snprintf(trace_buf, sizeof(trace_buf), "apk file %s upgrade, remove pkg"
, file_detail_ptr[i].file_name);
ATRACE_BEGIN(trace_buf);
file_detail_ptr[i].mark_for_delete = NEED_MARK_FOR_DELETE_PKG;
close(fd);
ATRACE_END();
ATRACE_END(); // end for opening file
ATRACE_END(); //end for start_playback_thread enter
return NULL;
} else {
QLOGI("file %s modify time changed update db", file_detail_ptr[i].file_name);
//mark study_finish=0 and cache_dropped=1
snprintf(trace_buf, sizeof(trace_buf), "file %s modifie mark update"
, file_detail_ptr[i].file_name);
ATRACE_BEGIN(trace_buf);
//restudy if file modify time changed for .odex and .dex file
if (strstr(file_detail_ptr[i].file_name, ".odex") != NULL
|| strstr(file_detail_ptr[i].file_name, ".dex") != NULL
|| strstr(file_detail_ptr[i].file_name, ".vdex") != NULL)
{
file_detail_ptr[i].mark_for_delete = NEED_MARK_FOR_UPDATE;
file_detail_ptr[i].file_modify_time = file_stat.st_mtime;
file_detail_ptr[i].filesize = file_stat.st_size;
drop_file_cache(fd, &(file_detail_ptr[i]));
} else {//disable unstable file
file_detail_ptr[i].mark_for_delete = NEED_MARK_FOR_DISABLE;
file_detail_ptr[i].disabled = 1;
}
ATRACE_END();
}
close(fd);
continue;
}
//try if need drop cache or playback
if (file_detail_ptr[i].study_finish == 0) {
QLOGI("Drop cache of file %s", file_detail_ptr[i].file_name);
snprintf(trace_buf, sizeof(trace_buf), "Drop cache of file %s",
file_detail_ptr[i].file_name);
ATRACE_BEGIN(trace_buf);
drop_file_cache(fd, &(file_detail_ptr[i]));
file_detail_ptr[i].cache_dropped = NEED_MARK_CACHE_DROPPED;
ATRACE_END();
} else {
QLOGI("io preload of file %s", file_detail_ptr[i].file_name);
snprintf(trace_buf, sizeof(trace_buf), "add file %s %.2f / %.2f MB to task",
file_detail_ptr[i].file_name,
file_detail_ptr[i].file_iop_size/1024.0/1024,
file_detail_ptr[i].filesize/1024.0/1024);
ATRACE_BEGIN(trace_buf);
iop_fileplayback_run(file_detail_ptr[i],file_stat,fd);
ATRACE_END();
if (file_detail_ptr[i].cache_dropped) {
file_detail_ptr[i].cache_dropped = NEED_MARK_CACHE_NOT_DROPPED;
}
}
close(fd);
if(total_data > MAX_TOTAL_DATA)
{
QLOGI("Max total size limit reached Total = %d",total_data);
//STOP reading and max total reached
break;
}
ATRACE_END(); // end for opening file
}
commit_filelist_info(file_detail_ptr, num_file);
delete_mark_files();
ATRACE_END(); //end for start_playback_thread enter
QLOGI(" Total Data = %.2f MB", total_data/1024.0/1024);
for (i=0; i<num_of_files; i++) {
if (file_detail_ptr[i].mincore_array) {
free(file_detail_ptr[i].mincore_array);
file_detail_ptr[i].mincore_array=NULL;
}
}
free(pkg_name);
free(file_detail_ptr);
return NULL;
}
/* Run playback operation */
static void *iop_fileplayback_run(file_details info, struct stat file_stat, int fd)
{
int page_itrr;
int total_mincore_pages = (info.file_iop_size+PAGE_SIZE-1)/PAGE_SIZE;
int total_pages = (file_stat.st_size+PAGE_SIZE-1)/PAGE_SIZE;
char *mincore_array = info.mincore_array;
char trace_buf[1024];
if (total_mincore_pages > 0) {
snprintf(trace_buf, sizeof(trace_buf), "%s read size:%.4fMB",
info.file_name,
total_mincore_pages*4/1024.0);
}
else
{
snprintf(trace_buf, sizeof(trace_buf), "%s read size:%.4fMB",
info.file_name,file_stat.st_size/1024.0/1024);
}
ATRACE_BEGIN(trace_buf);
int count_pages=0;
int read_data = 0;
int ret = 0;
QLOGI("MINCORE, PLAYBACK %s START, mincore_array: %p",
info.file_name, mincore_array);
//load each page by posix_fadvise here to avoid read_ahead
//and decrease CPU usage of copy io content result to local viriable
if (mincore_array == NULL) {
//if all file need read to cache
int len_size = file_stat.st_size;
ret = posix_fadvise(fd, 0, len_size, POSIX_FADV_WILLNEED);
read_data += len_size;
if (ret !=0) {
QLOGE("posix_fadvise failed: %s", strerror(errno));
}
} else {//load file content based on mincore result
count_pages=0;
for(page_itrr = 0; page_itrr < total_pages; page_itrr++) {
if (mincore_array[page_itrr] & 0x1) {
if (count_pages>=total_mincore_pages)
break;
ret=posix_fadvise(fd, page_itrr*PAGE_SIZE, PAGE_SIZE, POSIX_FADV_WILLNEED);
if (ret !=0) {
QLOGE("posix_fadvise failed: %s", strerror(errno));
}
read_data += PAGE_SIZE;
count_pages++;
}
}
}
//after finish preload, mark file cache advise as normal
ret = posix_fadvise(fd, 0, file_stat.st_size, POSIX_FADV_NORMAL);
if (ret !=0) {
QLOGE("posix_fadvise failed: %s", strerror(errno));
}
close(fd);
total_data += read_data;
QLOGI("MINCORE, PLAYBACK %s END real read file size = %.4fMB",
info.file_name, read_data/1024.0/1024);
ATRACE_END();
return NULL;
}
参考文档
将RandomAccessFile、FileChannel、MappedByteBuffer分别讲解了一遍:blog.csdn.net/xiaoduanayu…
posix_fadvise的man手册解释:man7.org/linux/man-p…