feat(bin): parse tag for logcat

refs #424
This commit is contained in:
Simon Chan 2022-06-07 00:07:50 +08:00
parent ca551a2414
commit 6158745ef5
No known key found for this signature in database
GPG key ID: A8B69F750B9BCEDD
3 changed files with 65 additions and 30 deletions

View file

@ -1,7 +1,7 @@
import { ICommandBarItemProps, Stack, StackItem } from "@fluentui/react"; import { ICommandBarItemProps, Stack, StackItem } from "@fluentui/react";
import { makeStyles, mergeClasses, shorthands } from "@griffel/react"; import { makeStyles, mergeClasses, shorthands } from "@griffel/react";
import { AbortController, decodeUtf8, ReadableStream, WritableStream } from '@yume-chan/adb'; import { AbortController, ReadableStream, WritableStream } from '@yume-chan/adb';
import { Logcat, LogMessage, LogPriority } from '@yume-chan/android-bin'; import { AndroidLogEntry, AndroidLogPriority, Logcat } from '@yume-chan/android-bin';
import { action, autorun, makeAutoObservable, observable, runInAction } from "mobx"; import { action, autorun, makeAutoObservable, observable, runInAction } from "mobx";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { NextPage } from "next"; import { NextPage } from "next";
@ -45,9 +45,8 @@ export interface Column extends GridColumn {
title: string; title: string;
} }
export interface LogRow extends LogMessage { export interface LogRow extends AndroidLogEntry {
timeString?: string; timeString?: string;
payloadString?: string;
} }
const state = makeAutoObservable({ const state = makeAutoObservable({
@ -57,7 +56,7 @@ const state = makeAutoObservable({
flushRequested: false, flushRequested: false,
list: [] as LogRow[], list: [] as LogRow[],
count: 0, count: 0,
stream: undefined as ReadableStream<LogMessage> | undefined, stream: undefined as ReadableStream<AndroidLogEntry> | undefined,
stopSignal: undefined as AbortController | undefined, stopSignal: undefined as AbortController | undefined,
selectedCount: 0, selectedCount: 0,
animationFrameId: undefined as number | undefined, animationFrameId: undefined as number | undefined,
@ -166,7 +165,7 @@ const state = makeAutoObservable({
} }
}, },
{ {
width: 80, width: 60,
title: 'PID', title: 'PID',
CellComponent: ({ rowIndex, columnIndex, className, ...rest }) => { CellComponent: ({ rowIndex, columnIndex, className, ...rest }) => {
const item = this.list[rowIndex]; const item = this.list[rowIndex];
@ -181,7 +180,7 @@ const state = makeAutoObservable({
} }
}, },
{ {
width: 80, width: 60,
title: 'TID', title: 'TID',
CellComponent: ({ rowIndex, columnIndex, className, ...rest }) => { CellComponent: ({ rowIndex, columnIndex, className, ...rest }) => {
const item = this.list[rowIndex]; const item = this.list[rowIndex];
@ -196,7 +195,7 @@ const state = makeAutoObservable({
} }
}, },
{ {
width: 100, width: 80,
title: 'Priority', title: 'Priority',
CellComponent: ({ rowIndex, columnIndex, className, ...rest }) => { CellComponent: ({ rowIndex, columnIndex, className, ...rest }) => {
const item = this.list[rowIndex]; const item = this.list[rowIndex];
@ -205,7 +204,22 @@ const state = makeAutoObservable({
return ( return (
<div className={mergeClasses(classes.code, className)} {...rest}> <div className={mergeClasses(classes.code, className)} {...rest}>
{LogPriority[item.priority]} {AndroidLogPriority[item.priority]}
</div>
);
}
},
{
width: 300,
title: 'Tag',
CellComponent: ({ rowIndex, columnIndex, className, ...rest }) => {
const item = this.list[rowIndex];
const classes = useClasses();
return (
<div className={mergeClasses(classes.code, className)} {...rest}>
{item.tag}
</div> </div>
); );
} }
@ -213,18 +227,14 @@ const state = makeAutoObservable({
{ {
width: 300, width: 300,
flexGrow: 1, flexGrow: 1,
title: 'Payload', title: 'Message',
CellComponent: ({ rowIndex, columnIndex, className, ...rest }) => { CellComponent: ({ rowIndex, columnIndex, className, ...rest }) => {
const item = this.list[rowIndex]; const item = this.list[rowIndex];
if (!item.payloadString) {
item.payloadString = decodeUtf8(item.payload);
}
const classes = useClasses(); const classes = useClasses();
return ( return (
<div className={mergeClasses(classes.code, className)} {...rest}> <div className={mergeClasses(classes.code, className)} {...rest}>
{item.payloadString} {item.message}
</div> </div>
); );
} }

View file

@ -16,7 +16,7 @@ export enum DemoModeSignalStrength {
Level4 = '4', Level4 = '4',
} }
// https://cs.android.com/android/platform/superproject/+/master:frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java;l=1073 // https://cs.android.com/android/platform/superproject/+/master:frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java;l=1362;drc=3b775bf7ad89902f94e03d191b0d8fbdebf2bdbf
export const DemoModeMobileDataTypes = ['1x', '3g', '4g', '4g+', '5g', '5ge', '5g+', export const DemoModeMobileDataTypes = ['1x', '3g', '4g', '4g+', '5g', '5ge', '5g+',
'e', 'g', 'h', 'h+', 'lte', 'lte+', 'dis', 'not', 'null'] as const; 'e', 'g', 'h', 'h+', 'lte', 'lte+', 'dis', 'not', 'null'] as const;

View file

@ -1,7 +1,7 @@
// cspell: ignore logcat // cspell: ignore logcat
import { AdbCommandBase, AdbSubprocessNoneProtocol, BufferedStream, BufferedStreamEndedError, DecodeUtf8Stream, ReadableStream, SplitLineStream, WritableStream } from "@yume-chan/adb"; import { AdbCommandBase, AdbSubprocessNoneProtocol, BufferedStream, BufferedStreamEndedError, DecodeUtf8Stream, ReadableStream, SplitLineStream, WritableStream } from "@yume-chan/adb";
import Struct, { StructAsyncDeserializeStream } from "@yume-chan/struct"; import Struct, { decodeUtf8, StructAsyncDeserializeStream } from "@yume-chan/struct";
// `adb logcat` is an alias to `adb shell logcat` // `adb logcat` is an alias to `adb shell logcat`
// so instead of adding to core library, it's implemented here // so instead of adding to core library, it's implemented here
@ -19,7 +19,8 @@ export enum LogId {
Kernel, Kernel,
} }
export enum LogPriority { // https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/include/android/log.h;l=73;drc=82b5738732161dbaafb2e2f25cce19cd26b9157d
export enum AndroidLogPriority {
Unknown, Unknown,
Default, Default,
Verbose, Verbose,
@ -56,21 +57,45 @@ export const LoggerEntry =
export type LoggerEntry = typeof LoggerEntry['TDeserializeResult']; export type LoggerEntry = typeof LoggerEntry['TDeserializeResult'];
export interface LogMessage extends LoggerEntry { // https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/logprint.cpp;drc=bbe77d66e7bee8bd1f0bc7e5492b5376b0207ef6;bpv=0
priority: LogPriority; export interface AndroidLogEntry extends LoggerEntry {
payload: Uint8Array; priority: AndroidLogPriority;
tag: string;
message: string;
} }
export async function deserializeLogMessage(stream: StructAsyncDeserializeStream): Promise<LogMessage> { function findTagEnd(payload: Uint8Array) {
const entry = await LoggerEntry.deserialize(stream); for (const separator of [0, ' '.charCodeAt(0), ':'.charCodeAt(0)]) {
const index = payload.indexOf(separator);
if (index !== -1) {
return index;
}
}
const index = payload.findIndex(x => x >= 0x7f);
if (index !== -1) {
return index;
}
return payload.length;
}
export async function deserializeAndroidLogEntry(stream: StructAsyncDeserializeStream): Promise<AndroidLogEntry> {
const entry = await LoggerEntry.deserialize(stream) as unknown as AndroidLogEntry;
if (entry.headerSize !== LoggerEntry.size) { if (entry.headerSize !== LoggerEntry.size) {
await stream.read(entry.headerSize - LoggerEntry.size); await stream.read(entry.headerSize - LoggerEntry.size);
} }
const priority = (await stream.read(1))[0] as LogPriority; let payload = await stream.read(entry.payloadSize);
const payload = await stream.read(entry.payloadSize - 1);
(entry as any).priority = priority; // https://cs.android.com/android/platform/superproject/+/master:system/logging/logcat/logcat.cpp;l=193-194;drc=bbe77d66e7bee8bd1f0bc7e5492b5376b0207ef6
(entry as any).payload = payload; // TODO: payload for some log IDs are in binary format.
return entry as LogMessage; entry.priority = payload[0] as AndroidLogPriority;
payload = payload.subarray(1);
const tagEnd = findTagEnd(payload);
entry.tag = decodeUtf8(payload.subarray(0, tagEnd));
entry.message = tagEnd < payload.length - 1 ? decodeUtf8(payload.subarray(tagEnd + 1)) : '';
return entry;
} }
export interface LogSize { export interface LogSize {
@ -158,7 +183,7 @@ export class Logcat extends AdbCommandBase {
]); ]);
} }
public binary(options?: LogcatOptions): ReadableStream<LogMessage> { public binary(options?: LogcatOptions): ReadableStream<AndroidLogEntry> {
let bufferedStream: BufferedStream; let bufferedStream: BufferedStream;
return new ReadableStream({ return new ReadableStream({
start: async () => { start: async () => {
@ -175,7 +200,7 @@ export class Logcat extends AdbCommandBase {
}, },
async pull(controller) { async pull(controller) {
try { try {
const entry = await deserializeLogMessage(bufferedStream); const entry = await deserializeAndroidLogEntry(bufferedStream);
controller.enqueue(entry); controller.enqueue(entry);
} catch (e) { } catch (e) {
if (e instanceof BufferedStreamEndedError) { if (e instanceof BufferedStreamEndedError) {