ProtoLog

هدف سیستم گزارش‌گیری Android دستیابی به دسترسی جهانی و سهولت استفاده است، با این فرض که همه داده‌های گزارش می‌توانند به‌صورت توالی نویسه‌ها نمایش داده شوند. این فرض با اکثر موارد استفاده، به‌ویژه زمانی که خوانایی گزارش بدون ابزارهای تخصصی حیاتی است، مطابقت دارد. بااین‌حال، در محیط‌هایی که به عملکرد ثبت گزارش بالا و اندازه‌های گزارش محدود نیاز دارند، ثبت گزارش مبتنی بر نوشتار بهینه نیست. یکی از این سناریوها WindowManager است که به سیستم گزارش‌گیری قدرتمندی نیاز دارد که گزارش‌های انتقال پنجره را در زمان واقعی با کمترین تأثیر بر سیستم مدیریت کند.

‫ProtoLog جایگزینی برای برطرف کردن نیازهای ثبت گزارش WindowManager و سرویس‌های مشابه است. ProtoLog مزایای زیر را نسبت به logcat ارائه می‌دهد:

  • از منابع کمتری برای گزارش‌گیری استفاده می‌کند.
  • از دیدگاه توسعه‌دهنده، این چارچوب همانند چارچوب گزارش‌گیری پیش‌فرض Android عمل می‌کند.
  • به شما امکان می‌دهد دستورات گزارش را در زمان اجرا فعال یا غیرفعال کنید.
  • می‌تواند در logcat هم ثبت کند.

‫ProtoLog برای بهینه‌سازی استفاده از حافظه، از سازوکار میان‌گذاری رشته استفاده می‌کند. این سازوکار درهم‌سازی کامپایل‌شده پیام را محاسبه و ذخیره می‌کند. برای بهبود عملکرد، ProtoLog درطول تدوین برای خدمات سیستم، رشته‌های داخلی را اجرا می‌کند. این ویژگی فقط شناسه پیام و آرگومان‌ها را در زمان اجرا ضبط می‌کند. وقتی پیگیری ProtoLog تولید می‌کنید یا گزارش اشکال دریافت می‌کنید، ProtoLog به‌طور خودکار واژه‌نامه پیام ایجادشده در زمان ترجمه را ادغام می‌کند. این کار باعث فعال شدن رمزگشایی پیام از هر ساختمانی می‌شود.

‫ProtoLog پیام‌ها را در قالب دودویی (proto) در ردیابی Perfetto ذخیره می‌کند. رمزگشایی پیام در trace_processor Perfetto انجام می‌شود. این فرایند پیام‌های پروتو باینری را کدگشایی می‌کند، شناسه‌های پیام را بااستفاده از فرهنگ لغت پیام جاسازی‌شده به رشته تبدیل می‌کند، و رشته را بااستفاده از متغیر مستقل‌های پویا قالب‌بندی می‌کند.

‫ProtoLog از همان سطوح گزارش android.utils.Log پشتیبانی می‌کند، که عبارت‌اند از d، v،‏ i،‏ w،‏ e، و wtf.

ProtoLog سمت کارخواه

در ابتدا، ProtoLog فقط برای سمت سرور WindowManager درنظر گرفته شده بود و در یک فرایند و عنصر واحد عمل می‌کرد. بعداً، این کد گسترش یافت تا کد پوسته WindowManager را در فرایند «میانای کاربر سیستم» دربر بگیرد. بااین‌حال، استفاده از ProtoLog به کد راه‌اندازی پیچیده و تکراری نیاز داشت. علاوه‌براین، ثبت Proto به سرور سیستم و فرایندهای «میانای کاربر سامانه» محدود شده بود. این امر ادغام آن در فرایندهای دیگر را دشوار می‌کرد و برای هرکدام به راه‌اندازی بافر حافظه جداگانه نیاز داشت. ProtoLog اکنون برای کد سمت کارخواه دردسترس است و نیاز به کد متن استاندارد اضافی را برطرف می‌کند.

برخلاف کد خدمات سیستم، کد سمت کارخواه معمولاً از رشته زمان ترجمه صرف‌نظر می‌کند. درعوض، رشته‌سازی به‌صورت پویا در یک رشته پس‌زمینه انجام می‌شود. درنتیجه، درحالی‌که ProtoLog سمت مشتری مزایای استفاده از حافظه مشابه با ProtoLog در سرویس‌های سیستم ارائه می‌دهد، سربار عملکردی کمی بالاتر دارد و مزیت کاهش حافظه حافظه سنجاق‌شده که همتای سمت سرور آن ارائه می‌دهد را ندارد.

گروه‌های ProtoLog

پیام‌های ProtoLog در گروه‌هایی به نام ProtoLogGroups سازمان‌دهی می‌شوند، مشابه نحوه سازمان‌دهی پیام‌های Logcat براساس TAG. این ProtoLogGroups به‌عنوان خوشه‌های پیام عمل می‌کنند که می‌توانید آن‌ها را در زمان اجرا فعال یا غیرفعال کنید. آن‌ها همچنین کنترل می‌کنند که آیا پیام‌ها درطول گردآوری حذف شوند یا نه و در کجا ثبت شوند (proto،‏ logcat، یا هر دو). هر ProtoLogGroup شامل ویژگی‌های زیر است:

  • ‫enabled: وقتی روی false تنظیم شود، پیام‌های این گروه درطول گردآوری مستثنا می‌شوند و در زمان اجرا دردسترس نیستند.
  • logToProto: مشخص می‌کند که آیا این گروه با قالب دودویی گزارش می‌کند یا نه.
  • logToLogcat: مشخص می‌کند که آیا این گروه در logcat ثبت می‌شود یا نه.
  • ‫tag: نام منبع پیام ثبت‌شده.

هر فرایندی که از ProtoLog استفاده می‌کند باید نمونه ProtoLogGroup پیکربندی‌شده داشته باشد.

انواع متغیر مستقل پشتیبانی‌شده

ProtoLog رشته‌ها را به‌طور داخلی بااستفاده از android.text.TextUtils#formatSimple(String, Object...) قالب‌بندی می‌کند، بنابراین نحو آن یکسان است.

ProtoLog از انواع آرگومان‌های زیر پشتیبانی می‌کند:

  • ‫%b - مقدار منطقی
  • ‫%d، %x - نوع انتگرال (کوتاه، عدد صحیح، یا طولانی)
  • %f - نوع نقطه شناور (float یا double)
  • ‫%s - رشته
  • ‫%% - نویسه درصد واقعی

اصلاح‌گرهای پهنا و دقت، مانند %04d و %10b، پشتیبانی می‌شوند. بااین‌حال، argument_index و flags پشتیبانی نمی‌شوند.

استفاده از ProtoLog در سرویس جدید

برای استفاده از ProtoLog در سرویس جدید، این مراحل را دنبال کنید:

  1. تعریف ProtoLogGroup برای این سرویس ایجاد کنید.
  2. تعریف را قبل‌از اولین استفاده مقداردهی اولیه کنید. برای مثال، آن را درطول ایجاد فرایند مقداردهی اولیه کنید:

    ProtoLog.init(ProtoLogGroup.values());
    
  3. از ProtoLog به همان روشی که از android.util.Log استفاده می‌کنید استفاده کنید:

    ProtoLog.v(WM_SHELL_STARTING_WINDOW, "create taskSnapshot surface for task: %d", taskId);
    

فعال کردن بهینه‌سازی زمان ترجمه

برای فعال کردن ProtoLog زمان ترجمه در یک فرایند، باید قوانین ساخت آن را تغییر دهید و باینری protologtool را فراخوانی کنید.

‫ProtoLogTool یک تبدیل کد باینری است که درونی‌سازی رشته را انجام می‌دهد و فراخوانی ProtoLog را به‌روز می‌کند. این دودویی هر ProtoLog گزارش تماس را همان‌طور که در این مثال نشان داده شده است تبدیل می‌کند:

ProtoLog.x(ProtoLogGroup.GROUP_NAME, "Format string %d %s", value1, value2);

به:

if (ProtoLogImpl.isEnabled(GROUP_NAME)) {
    int protoLogParam0 = value1;
    String protoLogParam1 = String.valueOf(value2);
    ProtoLogImpl.x(ProtoLogGroup.GROUP_NAME, 1234560b0100, protoLogParam0, protoLogParam1);
}

در این مثال، ProtoLog، ProtoLogImpl، و ProtoLogGroup کلاس‌هایی هستند که به‌عنوان آرگومان ارائه شده‌اند (می‌توانند وارد شوند، به‌صورت ایستا وارد شوند، یا مسیر کامل باشند، واردات کارت عام مجاز نیست) و x روش ثبت گزارش است.

تبدیل در سطح منبع انجام می‌شود. از رشته قالب، سطح گزارش، و نام گروه گزارش، درهم‌سازی تولید می‌شود و پس‌از ProtoLogGroup آرگومان درج می‌شود. کد تولیدشده واقعی درخط قرار می‌گیرد و تعدادی نویسه خط جدید اضافه می‌شود تا شماره‌گذاری خط در فایل حفظ شود.

مثال:

genrule {
    name: "wm_shell_protolog_src",
    srcs: [
        ":protolog-impl", // protolog lib
        ":wm_shell_protolog-groups", // protolog groups declaration
        ":wm_shell-sources", // source code
    ],
    tools: ["protologtool"],
    cmd: "$(location protologtool) transform-protolog-calls " +
        "--protolog-class com.android.internal.protolog.ProtoLog " +
        "--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
        "--loggroups-jar $(location :wm_shell_protolog-groups) " +
        "--viewer-config-file-path /system_ext/etc/wmshell.protolog.pb " +
        "--legacy-viewer-config-file-path /system_ext/etc/wmshell.protolog.json.gz " +
        "--legacy-output-file-path /data/misc/wmtrace/shell_log.winscope " +
        "--output-srcjar $(out) " +
        "$(locations :wm_shell-sources)",
    out: ["wm_shell_protolog.srcjar"],
}

گزینه‌های خط فرمان

یکی از مزایای اصلی ProtoLog این است که می‌توانید آن را در زمان اجرا فعال یا غیرفعال کنید. برای مثال، می‌توانید گزارش‌گیری پرحرف‌تری در ساخت داشته باشید که به‌طور پیش‌فرض غیرفعال است و آن را درطول توسعه محلی برای اشکال‌زدایی مشکل خاصی فعال کنید. این الگو، برای نمونه، در WindowManager با گروه‌های WM_DEBUG_WINDOW_TRANSITIONS و WM_DEBUG_WINDOW_TRANSITIONS_MIN فعال‌سازی انواع مختلف گزارش‌گیری انتقال استفاده می‌شود، که اولی به‌طور پیش‌فرض فعال است.

هنگام شروع ردیابی، می‌توانید ProtoLog را بااستفاده از Perfetto پیکربندی کنید. همچنین می‌توانید ProtoLog را به‌صورت محلی بااستفاده از خط فرمان adb پیکربندی کنید.

فرمان adb shell cmd protolog_configuration از آرگومان‌های زیر پشتیبانی می‌کند:

help
  Print this help text.

groups (list | status)
  list - lists all ProtoLog groups registered with ProtoLog service"
  status <group> - print the status of a ProtoLog group"

logcat (enable | disable) <group>"
  enable or disable ProtoLog to logcat

نکته‌هایی برای استفاده مؤثر

ProtoLog از رشته‌های داخلی برای پیام و هرگونه آرگومان رشته‌ای ارسال‌شده استفاده می‌کند. این یعنی برای بهره‌مندی بیشتر از ProtoLog، پیام‌ها باید مقادیر تکراری را در متغیرها جدا کنند.

برای مثال، بیانیه زیر را درنظر بگیرید:

Protolog.v(MY_GROUP, "%s", "The argument value is " + argument);

وقتی در زمان ترجمه بهینه‌سازی شود، به این ترجمه می‌شود:

ProtologImpl.v(MY_GROUP, 0x123, "The argument value is " + argument);

اگر از ProtoLog در کد با آرگومان‌های A,B,C استفاده شود:

Protolog.v(MY_GROUP, "%s", "The argument value is A");
Protolog.v(MY_GROUP, "%s", "The argument value is B");
Protolog.v(MY_GROUP, "%s", "The argument value is C");
Protolog.v(MY_GROUP, "%s", "The argument value is A");

این کار منجر به پیام‌های زیر در حافظه می‌شود:

Dict:
  0x123: "%s"
  0x111: "The argument value is A"
  0x222: "The argument value is B"
  0x333: "The argument value is C"

Message1 (Hash: 0x123, Arg1: 0x111)
Message2 (Hash: 0x123, Arg2: 0x222)
Message3 (Hash: 0x123, Arg3: 0x333)
Message4 (Hash: 0x123, Arg1: 0x111)

اگر درعوض، عبارت ProtoLog به‌صورت زیر نوشته شده بود:

Protolog.v(MY_GROUP, "The argument value is %s", argument);

بافر درون حافظه به این صورت خواهد بود:

Dict:
  0x123: "The argument value is %s" (24 b)
  0x111: "A" (1 b)
  0x222: "B" (1 b)
  0x333: "C" (1 b)

Message1 (Hash: 0x123, Arg1: 0x111)
Message2 (Hash: 0x123, Arg2: 0x222)
Message3 (Hash: 0x123, Arg3: 0x333)
Message4 (Hash: 0x123, Arg1: 0x111)

این توالی منجر به ردپای حافظه‌ای می‌شود که ۳۵٪ کوچک‌تر است.

نمایشگر Winscope

برگه نمایشگر ProtoLog در Winscope ردیابی‌های ProtoLog را در قالب جدولی سازمان‌دهی می‌کند. می‌توانید ردگیری‌ها را براساس سطح گزارش، برچسب، فایل منبع (جایی که عبارت ProtoLog وجود دارد)، و محتوای پیام فیلتر کنید. همه ستون‌ها قابل فیلتر کردن هستند. با کلیک کردن روی مُهر زمان در ستون اول، خط زمان به مُهر زمان پیام منتقل می‌شود. علاوه‌براین، با کلیک کردن روی رفتن به زمان فعلی، جدول ProtoLog به مُهر زمان انتخاب‌شده در جدول زمانی برمی‌گردد:

نمایشگر ProtoLog

شکل ۱. نمایشگر ProtoLog