ProtoLog

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

ProtoLog جایگزینی برای رفع نیازهای ورود به سیستم WindowManager و خدمات مشابه است. مزایای اصلی ProtoLog نسبت به logcat عبارتند از:

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

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

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

ProtoLog از همان سطوح log مانند android.utils.Log پشتیبانی می کند، یعنی: d , v , i , w , e , wtf .

ProtoLog سمت کلاینت

در ابتدا، ProtoLog صرفاً برای سمت سرور WindowManager در نظر گرفته شده بود که در یک فرآیند و مؤلفه واحد عمل می کرد. متعاقباً، گسترش یافت تا کد پوسته WindowManager را در فرآیند UI سیستم در بر گیرد، اما استفاده از ProtoLog به کد راه اندازی دیگ بخار پیچیده نیاز داشت. علاوه بر این، ثبت پروتو به فرآیندهای سرور سیستم و سیستم UI محدود می‌شد، که گنجاندن آن در سایر فرآیندها را پر زحمت می‌کرد و نیاز به تنظیم بافر حافظه جداگانه برای هر کدام داشت. با این حال، 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 - نوع ممیز شناور (شناور یا دوتایی)
  • %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)

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

بیننده وینسکوپ

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

ProtoLog viewer

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