ระบบการบันทึกของ Android มีเป้าหมายเพื่อให้เข้าถึงได้แบบสากลและใช้งานง่าย โดยสมมติว่าข้อมูลบันทึกทั้งหมดสามารถแสดงเป็นลำดับอักขระได้ สมมติฐานนี้สอดคล้องกับ Use Case ส่วนใหญ่ โดยเฉพาะอย่างยิ่งเมื่อความอ่านง่ายของบันทึกเป็นสิ่งสําคัญโดยไม่มีเครื่องมือเฉพาะ อย่างไรก็ตาม ในสภาพแวดล้อมที่ต้องใช้ประสิทธิภาพการบันทึกที่สูงและขนาดบันทึกที่จำกัด การบันทึกแบบข้อความอาจไม่เหมาะ สถานการณ์หนึ่งๆ ดังกล่าวคือ WindowManager ซึ่งต้องใช้ระบบการบันทึกที่มีประสิทธิภาพซึ่งจัดการบันทึกการเปลี่ยนหน้าต่างแบบเรียลไทม์ได้โดยมีผลกระทบต่อระบบน้อยที่สุด
ProtoLog เป็นทางเลือกในการตอบสนองความต้องการในการบันทึกของ WindowManager และบริการที่คล้ายกัน ประโยชน์หลักของ ProtoLog เหนือกว่า logcat ได้แก่
- ปริมาณทรัพยากรที่ใช้สำหรับการบันทึกจะน้อยลง
- จากมุมมองของนักพัฒนาซอฟต์แวร์ การดำเนินการนี้จะเหมือนกับการใช้เฟรมเวิร์กการบันทึกเริ่มต้นของ Android
- รองรับการเปิดหรือปิดใช้คำสั่งบันทึกเมื่อรันไทม์
- ยังคงบันทึกลงใน Logcat ได้หากจำเป็น
ProtoLog ใช้กลไกการจัดเก็บสตริงชั่วคราวเพื่อเพิ่มประสิทธิภาพการใช้หน่วยความจํา ซึ่งเกี่ยวข้องกับการคํานวณและบันทึกแฮชที่คอมไพล์แล้วของข้อความ ProtoLog จะทำการรวมสตริงระหว่างการคอมไพล์ (สําหรับบริการของระบบ) เพื่อปรับปรุงประสิทธิภาพ โดยบันทึกเฉพาะตัวระบุข้อความและอาร์กิวเมนต์ที่รันไทม์ นอกจากนี้ เมื่อสร้างการติดตาม ProtoLog หรือรับรายงานข้อบกพร่อง ProtoLog จะรวมพจนานุกรมข้อความที่สร้างไว้ขณะคอมไพล์โดยอัตโนมัติ ซึ่งช่วยให้สามารถถอดรหัสข้อความจากบิลด์ใดก็ได้
เมื่อใช้ ProtoLog ระบบจะจัดเก็บข้อความในรูปแบบไบนารี (proto) ภายในการติดตามของ Perfetto การถอดรหัสข้อความจะเกิดขึ้นภายใน Perfetto's
trace_processor
กระบวนการนี้ประกอบด้วยการถอดรหัสข้อความโปรโตคอลแบบไบนารี การแปลตัวระบุข้อความเป็นสตริงโดยใช้พจนานุกรมข้อความที่ฝัง และการจัดรูปแบบสตริงโดยใช้อาร์กิวเมนต์แบบไดนามิก
ProtoLog รองรับระดับบันทึกเดียวกับ android.utils.Log
ได้แก่ d
, v
,
i
, w
, e
, wtf
ProtoLog ฝั่งไคลเอ็นต์
เดิมที ProtoLog มีไว้สำหรับฝั่งเซิร์ฟเวอร์ของ WindowManager เท่านั้น ซึ่งทำงานภายในกระบวนการและคอมโพเนนต์เดียว ต่อมา เราได้ขยายการทำงานให้ครอบคลุมโค้ดเชลล์ WindowManager ในกระบวนการ UI ของระบบ แต่การใช้ ProtoLog ต้องใช้โค้ดการตั้งค่าที่ซ้ำกันซึ่งซับซ้อน นอกจากนี้ การบันทึก Proto นั้นถูกจำกัดไว้สำหรับเซิร์ฟเวอร์ของระบบและกระบวนการ UI ของระบบ ทำให้การรวมเข้ากับกระบวนการอื่นๆ เป็นเรื่องยากและจำเป็นต้องมีการตั้งค่าบัฟเฟอร์หน่วยความจำแยกต่างหากสำหรับแต่ละรายการ อย่างไรก็ตาม ตอนนี้ ProtoLog พร้อมใช้งานสำหรับโค้ดฝั่งไคลเอ็นต์แล้ว จึงไม่จำเป็นต้องใช้โค้ดที่ซ้ำกันอีก
โดยทั่วไปแล้ว โค้ดฝั่งไคลเอ็นต์จะข้ามการจัดเก็บสตริงชั่วคราวเมื่อคอมไพล์ ซึ่งต่างจากโค้ดบริการของระบบ แต่การรวมสตริงจะเกิดขึ้นแบบไดนามิกในเธรดเบื้องหลังแทน ด้วยเหตุนี้ แม้ว่า ProtoLog ฝั่งไคลเอ็นต์จะให้ข้อได้เปรียบในการใช้งานหน่วยความจำที่เทียบเท่ากับ ProtoLog ในบริการของระบบ แต่ก็มีค่าใช้จ่ายเพิ่มเติมด้านประสิทธิภาพสูงกว่าเล็กน้อยและไม่มีข้อได้เปรียบในการลดหน่วยความจำของหน่วยความจำที่ปักหมุดไว้ของคู่ค้าฝั่งเซิร์ฟเวอร์
กลุ่ม ProtoLog
ระบบจะจัดระเบียบข้อความ ProtoLog เป็นกลุ่มที่เรียกว่า ProtoLogGroups
ซึ่งคล้ายกับการจัดระเบียบข้อความ Logcat เป็นกลุ่มโดย TAG
ProtoLogGroups
เหล่านี้ทำหน้าที่เป็นคลัสเตอร์ของข้อความที่เปิดหรือปิดใช้ร่วมกันได้เมื่อรันไทม์
นอกจากนี้ ยังควบคุมได้ว่าควรตัดข้อความออกระหว่างการคอมไพล์หรือไม่ และควรบันทึกข้อความไว้ที่ใด (proto, logcat หรือทั้ง 2 อย่าง) ProtoLogGroup
แต่ละรายการประกอบด้วยพร็อพเพอร์ตี้ต่อไปนี้
enabled
: เมื่อตั้งค่าเป็นfalse
ระบบจะไม่รวมข้อความในกลุ่มนี้ระหว่างการคอมไพล์และข้อความจะไม่พร้อมใช้งานเมื่อรันไทม์logToProto
: กําหนดว่ากลุ่มนี้จะบันทึกด้วยรูปแบบไบนารีหรือไม่logToLogcat
: กำหนดว่ากลุ่มนี้จะบันทึกลงในบันทึกบันทึกหรือไม่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
คือคลาสที่ระบุเป็นอาร์กิวเมนต์ (สามารถนําเข้า นําเข้าแบบคงที่ หรือนําเข้าแบบ Full Path แต่ไม่อนุญาตให้นําเข้าแบบไวลด์การ์ด) และ 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