ระบบการบันทึกของ Android มีเป้าหมายเพื่อการเข้าถึงและการใช้งานที่ง่ายสำหรับทุกคน โดยถือว่าข้อมูลบันทึกทั้งหมดสามารถแสดงเป็นลำดับ ของอักขระได้ สมมติฐานนี้สอดคล้องกับกรณีการใช้งานส่วนใหญ่ โดยเฉพาะอย่างยิ่งเมื่อความสามารถในการอ่านบันทึกมีความสําคัญอย่างยิ่งโดยไม่มีเครื่องมือเฉพาะ อย่างไรก็ตาม ในสภาพแวดล้อมที่ต้องการประสิทธิภาพการบันทึกสูงและจำกัดขนาดบันทึก การบันทึกแบบข้อความอาจไม่เหมาะสม สถานการณ์หนึ่งคือ WindowManager ซึ่งจำเป็นต้องมี ระบบบันทึกที่แข็งแกร่งซึ่งสามารถจัดการบันทึกการเปลี่ยนหน้าต่างแบบเรียลไทม์โดยมี ผลกระทบต่อระบบน้อยที่สุด
ProtoLog เป็นทางเลือกในการตอบสนองความต้องการด้านการบันทึกของ WindowManager และ บริการที่คล้ายกัน ข้อดีหลักๆ ของ ProtoLog เมื่อเทียบกับ Logcat มีดังนี้
- ปริมาณทรัพยากรที่ใช้สำหรับการบันทึกจะน้อยลง
- จากมุมมองของนักพัฒนาซอฟต์แวร์ การใช้ Logcat จะเหมือนกับการใช้เฟรมเวิร์กล็อก Android เริ่มต้น
- รองรับการเปิดหรือปิดใช้คำสั่งบันทึกที่รันไทม์
- ยังคงบันทึกไปยัง Logcat ได้หากจำเป็น
ProtoLog ใช้กลไกการจัดเก็บสตริงเพื่อเพิ่มประสิทธิภาพการใช้งานหน่วยความจำ ซึ่ง เกี่ยวข้องกับการคำนวณและบันทึกแฮชที่คอมไพล์แล้วของข้อความ ProtoLog จะทำการจัดเก็บสตริงซ้ำระหว่างการคอมไพล์ (สำหรับบริการของระบบ) เพื่อปรับปรุงประสิทธิภาพ โดยจะบันทึกเฉพาะตัวระบุข้อความและอาร์กิวเมนต์ในขณะรันไทม์ นอกจากนี้ เมื่อสร้างการติดตาม ProtoLog หรือรับรายงานข้อบกพร่อง ProtoLog จะรวมพจนานุกรมข้อความที่สร้างขึ้นในเวลาคอมไพล์โดยอัตโนมัติ ซึ่งจะช่วยให้ถอดรหัสข้อความจากบิลด์ใดก็ได้
เมื่อใช้ ProtoLog ระบบจะจัดเก็บข้อความในรูปแบบไบนารี (proto) ภายใน
การติดตาม Perfetto การถอดรหัสข้อความจะเกิดขึ้นภายใน trace_processor
ของ Perfetto
กระบวนการนี้ประกอบด้วยการถอดรหัสข้อความ Proto แบบไบนารี
การแปลตัวระบุข้อความเป็นสตริงโดยใช้พจนานุกรมข้อความที่ฝังไว้
และการจัดรูปแบบสตริงโดยใช้อาร์กิวเมนต์แบบไดนามิก
ProtoLog รองรับระดับบันทึกเดียวกันกับ android.utils.Log
ได้แก่ d
, v
,
i
, w
, e
, wtf
ProtoLog ฝั่งไคลเอ็นต์
ในตอนแรก ProtoLog มีไว้สำหรับฝั่งเซิร์ฟเวอร์ของ WindowManager เท่านั้น โดยทำงานภายในกระบวนการและคอมโพเนนต์เดียว ต่อมาได้ขยายให้ครอบคลุมโค้ดเชลล์ WindowManager ในกระบวนการ UI ของระบบ แต่การใช้ ProtoLog ต้องใช้โค้ดการตั้งค่าบอยเลอร์เพลตที่ซับซ้อน นอกจากนี้ การบันทึก Proto ยัง จำกัดไว้สำหรับกระบวนการของเซิร์ฟเวอร์ระบบและ UI ของระบบ ซึ่งทำให้การ รวมเข้ากับกระบวนการอื่นๆ เป็นเรื่องยาก และจำเป็นต้องตั้งค่าบัฟเฟอร์หน่วยความจำแยกต่างหาก สำหรับแต่ละกระบวนการ อย่างไรก็ตาม ตอนนี้ ProtoLog พร้อมใช้งานสําหรับโค้ดฝั่งไคลเอ็นต์ แล้ว ซึ่งช่วยลดความจําเป็นในการใช้โค้ดบอยเลอร์เพลตเพิ่มเติม
โค้ดฝั่งไคลเอ็นต์มักจะข้ามการจัดเก็บสตริงในหน่วยความจำชั่วคราวที่เวลาคอมไพล์ ซึ่งต่างจากโค้ดบริการของระบบ แต่การจัดเก็บสตริงจะเกิดขึ้นแบบไดนามิกในเธรดเบื้องหลังแทน ด้วยเหตุนี้ แม้ว่า ProtoLog ในฝั่งไคลเอ็นต์จะให้ข้อดีด้านการใช้หน่วยความจำที่เทียบเท่ากับ ProtoLog ในบริการของระบบ แต่ก็มีโอเวอร์เฮดด้านประสิทธิภาพที่สูงกว่าเล็กน้อยและไม่มีข้อดีในการลดหน่วยความจำของหน่วยความจำที่ตรึงไว้ของ ProtoLog ในฝั่งเซิร์ฟเวอร์
กลุ่ม ProtoLog
ข้อความ ProtoLog จะจัดเป็นกลุ่มที่เรียกว่า ProtoLogGroups
ซึ่งคล้ายกับวิธีที่ข้อความ Logcat จัดระเบียบ
โดย TAG
ProtoLogGroups
เหล่านี้ทำหน้าที่เป็นกลุ่มข้อความที่สามารถเปิดหรือปิดใช้ร่วมกันได้ในขณะรันไทม์
นอกจากนี้ ยังควบคุมได้ว่าจะลบข้อความออกในระหว่างการ
คอมไพล์หรือไม่ และควรบันทึกข้อความไว้ที่ใด (proto, logcat หรือทั้ง 2 อย่าง) แต่ละ
ProtoLogGroup
ประกอบด้วยพร็อพเพอร์ตี้ต่อไปนี้
enabled
: เมื่อตั้งค่าเป็นfalse
ระบบจะไม่รวมข้อความในกลุ่มนี้ในระหว่าง การคอมไพล์และจะไม่พร้อมใช้งานในขณะรันไทม์logToProto
: กำหนดว่ากลุ่มนี้จะบันทึกด้วยรูปแบบไบนารีหรือไม่logToLogcat
: กำหนดว่ากลุ่มนี้จะบันทึกลงใน Logcat หรือไม่tag
: ชื่อแหล่งที่มาของข้อความที่บันทึก
แต่ละกระบวนการที่ใช้ ProtoLog ต้องมีการกำหนดค่าอินสแตนซ์ ProtoLogGroup
ประเภทอาร์กิวเมนต์ที่รองรับ
ภายใน สตริงรูปแบบ ProtoLog จะใช้ android.text.TextUtils#formatSimple(String, Object...)
ไวยากรณ์จึงเหมือนกัน
ProtoLog รองรับอาร์กิวเมนต์ประเภทต่อไปนี้
%b
- บูลีน%d
,%x
- ประเภทจำนวนเต็ม (short, integer หรือ long)%f
- ประเภทจุดลอยตัว (float หรือ double)%s
- สตริง%%
- อักขระเปอร์เซ็นต์ลิเทอรัล
ระบบรองรับตัวปรับความกว้างและความแม่นยำ เช่น %04d
และ %10b
แต่ไม่รองรับ argument_index
และ flags
ใช้ ProtoLog ในบริการใหม่
วิธีใช้ ProtoLog ในกระบวนการใหม่
สร้าง
ProtoLogGroup
คำจำกัดความสำหรับบริการนี้เริ่มต้นความหมายก่อนใช้งานครั้งแรก (เช่น ในการสร้างกระบวนการ)
Protolog.init(ProtologGroup.values());
ใช้
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%
โปรแกรมดู Winscope
แท็บโปรแกรมดู ProtoLog ของ Winscope จะแสดงการติดตาม ProtoLog ที่จัดระเบียบในรูปแบบตาราง คุณสามารถกรองการติดตามตามระดับบันทึก แท็ก ไฟล์ต้นฉบับ (ที่มีคำสั่ง ProtoLog) และเนื้อหาข้อความ คอลัมน์ทั้งหมดกรองได้ การคลิกการประทับเวลาในคอลัมน์แรกจะนำไทม์ไลน์ไปยัง การประทับเวลาของข้อความ นอกจากนี้ การคลิกไปที่เวลาปัจจุบันจะเลื่อนตาราง ProtoLog กลับไปที่การประทับเวลาที่เลือกในไทม์ไลน์
รูปที่ 1 ผู้ดู ProtoLog