ART profiler

Add args to the installd commands for profiler usage.
Make installd create the profile files and allow apps
to write to them.

The profile files are in /data/dalvik-cache/profiles.  This
central location is needed due to permissions issues with
dex2oat reading from an app's data directory.  The solution
is to put the profile file in a directory owned by the
install user and make the file writeable by the shared group
id of for the app.  The app can read and write to these files
only in the profiles directory.  The 'system' user also needs
to be able to read the files in order to determine the amount
of change to the file over time.

Bug: 12877748
Change-Id: I9b8e59e3bd7df8a1bf60fa7ffd376a24ba0eb42f

Conflicts:
	cmds/installd/commands.c
This commit is contained in:
Dave Allison 2014-01-30 14:19:23 -08:00
parent 3c39e03c08
commit d93707342a
4 changed files with 112 additions and 37 deletions

View File

@ -1,16 +1,16 @@
/* /*
** Copyright 2008, The Android Open Source Project ** Copyright 2008, The Android Open Source Project
** **
** Licensed under the Apache License, Version 2.0 (the "License"); ** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License. ** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at ** You may obtain a copy of the License at
** **
** http://www.apache.org/licenses/LICENSE-2.0 ** http://www.apache.org/licenses/LICENSE-2.0
** **
** Unless required by applicable law or agreed to in writing, software ** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS, ** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and ** See the License for the specific language governing permissions and
** limitations under the License. ** limitations under the License.
*/ */
@ -115,6 +115,8 @@ int uninstall(const char *pkgname, userid_t userid)
if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, userid)) if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, userid))
return -1; return -1;
remove_profile_file(pkgname);
/* delete contents AND directory, no exceptions */ /* delete contents AND directory, no exceptions */
return delete_dir_contents(pkgdir, 1, NULL); return delete_dir_contents(pkgdir, 1, NULL);
} }
@ -557,9 +559,9 @@ int create_cache_path(char path[PKG_PATH_MAX], const char *src)
return -1; return -1;
} }
dstlen = srclen + strlen(DALVIK_CACHE_PREFIX) + dstlen = srclen + strlen(DALVIK_CACHE_PREFIX) +
strlen(DALVIK_CACHE_POSTFIX) + 1; strlen(DALVIK_CACHE_POSTFIX) + 1;
if (dstlen > PKG_PATH_MAX) { if (dstlen > PKG_PATH_MAX) {
return -1; return -1;
} }
@ -568,7 +570,7 @@ int create_cache_path(char path[PKG_PATH_MAX], const char *src)
DALVIK_CACHE_PREFIX, DALVIK_CACHE_PREFIX,
src + 1, /* skip the leading / */ src + 1, /* skip the leading / */
DALVIK_CACHE_POSTFIX); DALVIK_CACHE_POSTFIX);
for(tmp = path + strlen(DALVIK_CACHE_PREFIX); *tmp; tmp++) { for(tmp = path + strlen(DALVIK_CACHE_PREFIX); *tmp; tmp++) {
if (*tmp == '/') { if (*tmp == '/') {
*tmp = '@'; *tmp = '@';
@ -601,7 +603,7 @@ static void run_dexopt(int zip_fd, int odex_fd, const char* input_file_name,
} }
static void run_dex2oat(int zip_fd, int oat_fd, const char* input_file_name, static void run_dex2oat(int zip_fd, int oat_fd, const char* input_file_name,
const char* output_file_name) const char* output_file_name, const char *pkgname)
{ {
char dex2oat_flags[PROPERTY_VALUE_MAX]; char dex2oat_flags[PROPERTY_VALUE_MAX];
property_get("dalvik.vm.dex2oat-flags", dex2oat_flags, ""); property_get("dalvik.vm.dex2oat-flags", dex2oat_flags, "");
@ -613,17 +615,25 @@ static void run_dex2oat(int zip_fd, int oat_fd, const char* input_file_name,
char zip_location_arg[strlen("--zip-location=") + PKG_PATH_MAX]; char zip_location_arg[strlen("--zip-location=") + PKG_PATH_MAX];
char oat_fd_arg[strlen("--oat-fd=") + MAX_INT_LEN]; char oat_fd_arg[strlen("--oat-fd=") + MAX_INT_LEN];
char oat_location_arg[strlen("--oat-name=") + PKG_PATH_MAX]; char oat_location_arg[strlen("--oat-name=") + PKG_PATH_MAX];
char profile_file[strlen("--profile-file=") + PKG_PATH_MAX];
sprintf(zip_fd_arg, "--zip-fd=%d", zip_fd); sprintf(zip_fd_arg, "--zip-fd=%d", zip_fd);
sprintf(zip_location_arg, "--zip-location=%s", input_file_name); sprintf(zip_location_arg, "--zip-location=%s", input_file_name);
sprintf(oat_fd_arg, "--oat-fd=%d", oat_fd); sprintf(oat_fd_arg, "--oat-fd=%d", oat_fd);
sprintf(oat_location_arg, "--oat-location=%s", output_file_name); sprintf(oat_location_arg, "--oat-location=%s", output_file_name);
if (strcmp(pkgname, "*") != 0) {
snprintf(profile_file, sizeof(profile_file), "--profile-file=%s/%s",
DALVIK_CACHE_PREFIX "profiles", pkgname);
} else {
strcpy(profile_file, "--no-profile-file");
}
ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, input_file_name, output_file_name); ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, input_file_name, output_file_name);
execl(DEX2OAT_BIN, DEX2OAT_BIN, execl(DEX2OAT_BIN, DEX2OAT_BIN,
zip_fd_arg, zip_location_arg, zip_fd_arg, zip_location_arg,
oat_fd_arg, oat_location_arg, oat_fd_arg, oat_location_arg,
strlen(dex2oat_flags) > 0 ? dex2oat_flags : NULL, strlen(dex2oat_flags) > 0 ? dex2oat_flags : NULL,
profile_file,
(char*) NULL); (char*) NULL);
ALOGE("execl(%s) failed: %s\n", DEX2OAT_BIN, strerror(errno)); ALOGE("execl(%s) failed: %s\n", DEX2OAT_BIN, strerror(errno));
} }
@ -654,7 +664,8 @@ static int wait_child(pid_t pid)
} }
} }
int dexopt(const char *apk_path, uid_t uid, int is_public) int dexopt(const char *apk_path, uid_t uid, int is_public,
const char *pkgname)
{ {
struct utimbuf ut; struct utimbuf ut;
struct stat apk_stat, dex_stat; struct stat apk_stat, dex_stat;
@ -708,6 +719,12 @@ int dexopt(const char *apk_path, uid_t uid, int is_public)
goto fail; goto fail;
} }
// Create profile file if there is a package name present.
if (strcmp(pkgname, "*") != 0) {
create_profile_file(pkgname, uid);
}
ALOGV("DexInv: --- BEGIN '%s' ---\n", apk_path); ALOGV("DexInv: --- BEGIN '%s' ---\n", apk_path);
pid_t pid; pid_t pid;
@ -740,7 +757,7 @@ int dexopt(const char *apk_path, uid_t uid, int is_public)
if (strncmp(persist_sys_dalvik_vm_lib, "libdvm", 6) == 0) { if (strncmp(persist_sys_dalvik_vm_lib, "libdvm", 6) == 0) {
run_dexopt(zip_fd, out_fd, apk_path, out_path); run_dexopt(zip_fd, out_fd, apk_path, out_path);
} else if (strncmp(persist_sys_dalvik_vm_lib, "libart", 6) == 0) { } else if (strncmp(persist_sys_dalvik_vm_lib, "libart", 6) == 0) {
run_dex2oat(zip_fd, out_fd, apk_path, out_path); run_dex2oat(zip_fd, out_fd, apk_path, out_path, pkgname);
} else { } else {
exit(69); /* Unexpected persist.sys.dalvik.vm.lib value */ exit(69); /* Unexpected persist.sys.dalvik.vm.lib value */
} }
@ -804,12 +821,12 @@ int movefileordir(char* srcpath, char* dstpath, int dstbasepos,
int srcend = strlen(srcpath); int srcend = strlen(srcpath);
int dstend = strlen(dstpath); int dstend = strlen(dstpath);
if (lstat(srcpath, statbuf) < 0) { if (lstat(srcpath, statbuf) < 0) {
ALOGW("Unable to stat %s: %s\n", srcpath, strerror(errno)); ALOGW("Unable to stat %s: %s\n", srcpath, strerror(errno));
return 1; return 1;
} }
if ((statbuf->st_mode&S_IFDIR) == 0) { if ((statbuf->st_mode&S_IFDIR) == 0) {
mkinnerdirs(dstpath, dstbasepos, S_IRWXU|S_IRWXG|S_IXOTH, mkinnerdirs(dstpath, dstbasepos, S_IRWXU|S_IRWXG|S_IXOTH,
dstuid, dstgid, statbuf); dstuid, dstgid, statbuf);
@ -835,7 +852,7 @@ int movefileordir(char* srcpath, char* dstpath, int dstbasepos,
} }
res = 0; res = 0;
while ((de = readdir(d))) { while ((de = readdir(d))) {
const char *name = de->d_name; const char *name = de->d_name;
/* always skip "." and ".." */ /* always skip "." and ".." */
@ -843,32 +860,32 @@ int movefileordir(char* srcpath, char* dstpath, int dstbasepos,
if (name[1] == 0) continue; if (name[1] == 0) continue;
if ((name[1] == '.') && (name[2] == 0)) continue; if ((name[1] == '.') && (name[2] == 0)) continue;
} }
if ((srcend+strlen(name)) >= (PKG_PATH_MAX-2)) { if ((srcend+strlen(name)) >= (PKG_PATH_MAX-2)) {
ALOGW("Source path too long; skipping: %s/%s\n", srcpath, name); ALOGW("Source path too long; skipping: %s/%s\n", srcpath, name);
continue; continue;
} }
if ((dstend+strlen(name)) >= (PKG_PATH_MAX-2)) { if ((dstend+strlen(name)) >= (PKG_PATH_MAX-2)) {
ALOGW("Destination path too long; skipping: %s/%s\n", dstpath, name); ALOGW("Destination path too long; skipping: %s/%s\n", dstpath, name);
continue; continue;
} }
srcpath[srcend] = dstpath[dstend] = '/'; srcpath[srcend] = dstpath[dstend] = '/';
strcpy(srcpath+srcend+1, name); strcpy(srcpath+srcend+1, name);
strcpy(dstpath+dstend+1, name); strcpy(dstpath+dstend+1, name);
if (movefileordir(srcpath, dstpath, dstbasepos, dstuid, dstgid, statbuf) != 0) { if (movefileordir(srcpath, dstpath, dstbasepos, dstuid, dstgid, statbuf) != 0) {
res = 1; res = 1;
} }
// Note: we will be leaving empty directories behind in srcpath, // Note: we will be leaving empty directories behind in srcpath,
// but that is okay, the package manager will be erasing all of the // but that is okay, the package manager will be erasing all of the
// data associated with .apks that disappear. // data associated with .apks that disappear.
srcpath[srcend] = dstpath[dstend] = 0; srcpath[srcend] = dstpath[dstend] = 0;
} }
closedir(d); closedir(d);
return res; return res;
} }
@ -910,7 +927,7 @@ int movefiles()
UPDATE_COMMANDS_DIR_PREFIX, name); UPDATE_COMMANDS_DIR_PREFIX, name);
continue; continue;
} }
bufp = 0; bufp = 0;
bufe = 0; bufe = 0;
buf[PKG_PATH_MAX] = 0; buf[PKG_PATH_MAX] = 0;

View File

@ -38,8 +38,8 @@ static int do_install(char **arg, char reply[REPLY_MAX])
static int do_dexopt(char **arg, char reply[REPLY_MAX]) static int do_dexopt(char **arg, char reply[REPLY_MAX])
{ {
/* apk_path, uid, is_public */ /* apk_path, uid, is_public, pkgname */
return dexopt(arg[0], atoi(arg[1]), atoi(arg[2])); return dexopt(arg[0], atoi(arg[1]), atoi(arg[2]), arg[3]);
} }
static int do_move_dex(char **arg, char reply[REPLY_MAX]) static int do_move_dex(char **arg, char reply[REPLY_MAX])
@ -138,7 +138,7 @@ struct cmdinfo {
struct cmdinfo cmds[] = { struct cmdinfo cmds[] = {
{ "ping", 0, do_ping }, { "ping", 0, do_ping },
{ "install", 4, do_install }, { "install", 4, do_install },
{ "dexopt", 3, do_dexopt }, { "dexopt", 4, do_dexopt },
{ "movedex", 2, do_move_dex }, { "movedex", 2, do_move_dex },
{ "rmdex", 1, do_rm_dex }, { "rmdex", 1, do_rm_dex },
{ "remove", 2, do_remove }, { "remove", 2, do_remove },

View File

@ -189,6 +189,8 @@ char *build_string3(char *s1, char *s2, char *s3);
int ensure_dir(const char* path, mode_t mode, uid_t uid, gid_t gid); int ensure_dir(const char* path, mode_t mode, uid_t uid, gid_t gid);
int ensure_media_user_dirs(userid_t userid); int ensure_media_user_dirs(userid_t userid);
int create_profile_file(const char *pkgname, gid_t gid);
void remove_profile_file(const char *pkgname);
/* commands.c */ /* commands.c */
@ -207,7 +209,7 @@ int get_size(const char *pkgname, userid_t userid, const char *apkpath, const ch
const char *fwdlock_apkpath, const char *asecpath, int64_t *codesize, const char *fwdlock_apkpath, const char *asecpath, int64_t *codesize,
int64_t *datasize, int64_t *cachesize, int64_t *asecsize); int64_t *datasize, int64_t *cachesize, int64_t *asecsize);
int free_cache(int64_t free_size); int free_cache(int64_t free_size);
int dexopt(const char *apk_path, uid_t uid, int is_public); int dexopt(const char *apk_path, uid_t uid, int is_public, const char *pkgName);
int movefiles(); int movefiles();
int linklib(const char* target, const char* source, int userId); int linklib(const char* target, const char* source, int userId);
int idmap(const char *target_path, const char *overlay_path, uid_t uid); int idmap(const char *target_path, const char *overlay_path, uid_t uid);

View File

@ -1,16 +1,16 @@
/* /*
** Copyright 2008, The Android Open Source Project ** Copyright 2008, The Android Open Source Project
** **
** Licensed under the Apache License, Version 2.0 (the "License"); ** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License. ** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at ** You may obtain a copy of the License at
** **
** http://www.apache.org/licenses/LICENSE-2.0 ** http://www.apache.org/licenses/LICENSE-2.0
** **
** Unless required by applicable law or agreed to in writing, software ** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS, ** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and ** See the License for the specific language governing permissions and
** limitations under the License. ** limitations under the License.
*/ */
@ -1005,3 +1005,59 @@ int ensure_media_user_dirs(userid_t userid) {
return 0; return 0;
} }
int create_profile_file(const char *pkgname, gid_t gid) {
const char *profile_dir = DALVIK_CACHE_PREFIX "profiles";
struct stat profileStat;
char profile_file[PKG_PATH_MAX];
// If we don't have a profile directory under dalvik-cache we need to create one.
if (stat(profile_dir, &profileStat) < 0) {
// Create the profile directory under dalvik-cache.
if (mkdir(profile_dir, 0711) < 0) {
ALOGE("cannot make profile dir '%s': %s\n", profile_dir, strerror(errno));
return -1;
}
// Make the profile directory write-only for group and other. Owner can rwx it.
if (chmod(profile_dir, 0711) < 0) {
ALOGE("cannot chown profile dir '%s': %s\n", profile_dir, strerror(errno));
unlink(profile_dir);
return -1;
}
}
snprintf(profile_file, sizeof(profile_file), "%s/%s", profile_dir, pkgname);
// The 'system' user needs to be able to read the profile to determine if dex2oat
// needs to be run. This is done in dalvik.system.DexFile.isDexOptNeededInternal(). So
// we make it world readable. Not a problem since the dalvik cache is world
// readable anyway.
int fd = open(profile_file, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, 0664);
// Open will fail if the file already exists. We want to ignore that.
if (fd >= 0) {
if (fchown(fd, -1, gid) < 0) {
ALOGE("cannot chown profile file '%s': %s\n", profile_file, strerror(errno));
close(fd);
unlink(profile_file);
return -1;
}
if (fchmod(fd, 0664) < 0) {
ALOGE("cannot chmod profile file '%s': %s\n", profile_file, strerror(errno));
close(fd);
unlink(profile_file);
return -1;
}
close(fd);
}
return 0;
}
void remove_profile_file(const char *pkgname) {
char profile_file[PKG_PATH_MAX];
snprintf(profile_file, sizeof(profile_file), "%s/%s", DALVIK_CACHE_PREFIX "profiles", pkgname);
unlink(profile_file);
}