ב-Android בגרסה 8.0 ואילך, ממשק הכלים של ART (ART TI) חושף רכיבים פנימיים מסוימים בסביבת זמן הריצה, ומאפשר לכלי לניתוחי פרופיל ולכלי לניפוי באגים להשפיע על התנהגות האפליקציות בסביבת זמן הריצה. אפשר להשתמש בזה כדי להטמיע כלים מתקדמים למדידת ביצועים שמיועדים להטמעת סוכנים מקומיים בפלטפורמות אחרות.
הרכיבים הפנימיים של סביבת זמן הריצה חשופים לסוכנים שנטענו לתהליך של סביבת זמן הריצה.
הם מתקשרים עם ה-ART באמצעות קריאות ישירות וקריאות חוזרות. סביבת זמן הריצה תומכת במספר סוכנים, כך שאפשר להפריד בין בעיות שונות של פרופיל אורתוגונלי. אפשר לספק את הסוכנים בתחילת זמן הריצה (כשמפעילים את dalvikvm
או את app_process
) או לצרף אותם לתהליך שכבר פועל.
היכולת להתקין מכשירי מדידה ולשנות את ההתנהגות של האפליקציה ושל סביבת זמן הריצה היא חזקה מאוד, ולכן שולבו שני אמצעי בטיחות ב-ART TI:
- ראשית, הקוד שחשף את ממשק הסוכן, JVMTI, מיושם כפלאגין בסביבת זמן הריצה, ולא כרכיב ליבה של סביבת זמן הריצה. יכול להיות שטעינה של תוספים תהיה מוגבלת, כדי למנוע מהסוכנים למצוא את נקודות הממשק.
- שנית, גם הכיתה
ActivityManager
וגם תהליך זמן הריצה מאפשרים לסוכנים להתחבר רק לאפליקציות שאפשר לנפות בהן באגים. אפליקציות שניתן לנפות בהן באגים אושרו על ידי המפתחים שלהן לצורך ניתוח והתקנה של מכשירי מדידה, והן לא מופצות למשתמשים קצה. חנות Google Play לא מאפשרת להפיץ אפליקציות שאפשר לנפות באגים בהן. כך אפשר לוודא שאפליקציות רגילות (כולל רכיבי הליבה) לא ניתנות לשימוש בכלים למדידת ביצועים או לשינוי.
עיצוב
התהליך הכללי והקישורים באפליקציה עם המדדים מוצגים באיור 1.

הפלאגין של ART libopenjdkjvmti
חושף את ה-ART TI, שנועד לענות על הצרכים והאילוצים של הפלטפורמה:
- הגדרה מחדש של כיתות מבוססת על קובצי
Dex
שמכילים רק הגדרה אחת של כיתה, במקום קובצי כיתה. - ממשקי ה-API בשפת Java לצורכי מכשור והגדרה מחדש לא נחשפים.
ב-ART TI יש גם תמיכה בפרופילים של Android Studio.
טעינה או צירוף של סוכן
כדי לצרף סוכן בזמן ההפעלה בסביבת זמן הריצה, משתמשים בפקודה הזו כדי לטעון גם את הפלאגין של JVMTI וגם את הסוכן הנתון:
dalvikvm -Xplugin:libopenjdkjvmti.so -agentpath:/path/to/agent/libagent.so …
אין אמצעי בטיחות כשסוכן נטען בזמן ההפעלה של סביבת זמן הריצה, לכן חשוב לזכור שסביבת זמן ריצה שמופעלת באופן ידני מאפשרת שינוי מלא ללא אמצעי בטיחות. (כך אפשר לבדוק את ה-ART).
הערה: המדיניות הזו לא חלה על אפליקציות רגילות (כולל שרת המערכת) במכשיר. אפליקציות מתפצלות מ-zygote שכבר פועל, ותהליך zygote לא רשאי לטעון סוכנים.
כדי לצרף סוכן לאפליקציה שכבר פועלת, משתמשים בפקודה הבאה:
adb shell cmd activity attach-agent [process] /path/to/agent/libagent.so[=agent-options]
אם הפלאגין של JVMTI עדיין לא נטען, הצמדת סוכן תטען גם את הפלאגין וגם את ספריית הסוכן.
אפשר לצרף סוכן רק לאפליקציה שפועלת ומסומנת כניתנת לניפוי באגים (חלק מהמניפסט של האפליקציה, עם המאפיין android:debuggable
שמוגדר כ-true
בצומת של האפליקציה). גם הכיתה ActivityManager
וגם ה-ART מבצעים בדיקות לפני שמאפשרים לצרף סוכן. הכיתה ActivityManager בודקת את פרטי האפליקציה הנוכחיים (שנגזרים מהנתונים של הכיתה PackageManager) כדי לבדוק אם אפשר לנפות באפליקציה באגים, והזמן הריצה בודק את הסטטוס הנוכחי שלה, שהוגדר כשהאפליקציה הופעל.
מיקומי הנציגים
סביבת זמן הריצה צריכה לטעון את הסוכנים לתהליך הנוכחי, כדי שהסוכן יוכל לקשר אליו ישירות ולתקשר איתו. ה-ART עצמו לא תלוי במיקום הספציפי שממנו מגיע הסוכן. המחרוזת משמשת לשיחה עם dlopen
. הרשאות מערכת הקבצים וכללי המדיניות של SELinux מגבילים את הטעינה בפועל.
כדי לשלוח סוכנים שאפשר להריץ באפליקציה שניתן לנפות באגים בה, מבצעים את הפעולות הבאות:
- הטמעת הסוכן בספריית ה-APK של האפליקציה.
- משתמשים ב-
run-as
כדי להעתיק את הסוכן לספריית הנתונים של האפליקציה.
ממשקי API
השיטה הבאה נוספה ל-android.os.Debug
.
/** * Attach a library as a jvmti agent to the current runtime, with the given classloader * determining the library search path. * Note: agents may only be attached to debuggable apps. Otherwise, this function will * throw a SecurityException. * * @param library the library containing the agent. * @param options the options passed to the agent. * @param classLoader the classloader determining the library search path. * * @throws IOException if the agent could not be attached. * @throws a SecurityException if the app is not debuggable. */ public static void attachJvmtiAgent(@NonNull String library, @Nullable String options, @Nullable ClassLoader classLoader) throws IOException {
ממשקי API אחרים של Android
הפקודה attach-agent גלויה לכולם. הפקודה הזו מצרפת סוכן JVMTI לתהליך פעיל:
adb shell 'am attach-agent com.example.android.displayingbitmaps \'/data/data/com.example.android.displayingbitmaps/code_cache/libfieldnulls.so=Ljava/lang/Class;.name:Ljava/lang/String;\''
הפקודות am start -P
ו-am
start-profiler/stop-profiler
דומות לפקודה attach-agent.
JVMTI
התכונה הזו חושפת את ה-JVMTI API לסוכנים (קוד מקורי). היכולות החשובות כוללות:
- הגדרה מחדש של כיתה.
- מעקב אחר הקצאת אובייקטים ואיסוף אשפה.
- איטרציה על כל האובייקטים בערימה, לפי עץ ההפניות של האובייקטים.
- בדיקת סטאקים של קריאות ב-Java.
- השעיה (והמשך) של כל השרשורים.
יכול להיות שיכולות שונות יהיו זמינות בגרסאות שונות של Android.
תאימות
כדי להשתמש בתכונה הזו, צריך תמיכה בסביבת זמן ריצה של הליבה, שזמינה רק ב-Android מגרסה 8.0 ואילך. יצרני המכשירים לא צריכים לבצע שינויים כדי להטמיע את התכונה הזו. הוא חלק מ-AOSP.
אימות
ב-CTS נבדקים הנושאים הבאים ב-Android מגרסה 8 ואילך:
- הבדיקה בודקת שהסוכנים מצורפים לאפליקציות שניתן לנפות בהן באגים, ולא מצליחים לצרף אפליקציות שלא ניתן לנפות בהן באגים.
- בדיקה של כל ממשקי ה-API של JVMTI שהוגדרו
- בדיקה שהממשק הבינארי של הסוכנים יציב
בדיקות נוספות נוספו ל-Android 9 ואילך, והן נכללות בבדיקות CTS של הגרסאות האלה.