ระบบการบันทึกของ 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 ในกระบวนการ System 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