Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit 6158745e authored by Simon Chan's avatar Simon Chan
Browse files

feat(bin): parse tag for logcat

refs #424
parent ca551a24
Loading
Loading
Loading
Loading
+25 −15
Original line number Diff line number Diff line
import { ICommandBarItemProps, Stack, StackItem } from "@fluentui/react";
import { makeStyles, mergeClasses, shorthands } from "@griffel/react";
import { AbortController, decodeUtf8, ReadableStream, WritableStream } from '@yume-chan/adb';
import { Logcat, LogMessage, LogPriority } from '@yume-chan/android-bin';
import { AbortController, ReadableStream, WritableStream } from '@yume-chan/adb';
import { AndroidLogEntry, AndroidLogPriority, Logcat } from '@yume-chan/android-bin';
import { action, autorun, makeAutoObservable, observable, runInAction } from "mobx";
import { observer } from "mobx-react-lite";
import { NextPage } from "next";
@@ -45,9 +45,8 @@ export interface Column extends GridColumn {
    title: string;
}

export interface LogRow extends LogMessage {
export interface LogRow extends AndroidLogEntry {
    timeString?: string;
    payloadString?: string;
}

const state = makeAutoObservable({
@@ -57,7 +56,7 @@ const state = makeAutoObservable({
    flushRequested: false,
    list: [] as LogRow[],
    count: 0,
    stream: undefined as ReadableStream<LogMessage> | undefined,
    stream: undefined as ReadableStream<AndroidLogEntry> | undefined,
    stopSignal: undefined as AbortController | undefined,
    selectedCount: 0,
    animationFrameId: undefined as number | undefined,
@@ -166,7 +165,7 @@ const state = makeAutoObservable({
                }
            },
            {
                width: 80,
                width: 60,
                title: 'PID',
                CellComponent: ({ rowIndex, columnIndex, className, ...rest }) => {
                    const item = this.list[rowIndex];
@@ -181,7 +180,7 @@ const state = makeAutoObservable({
                }
            },
            {
                width: 80,
                width: 60,
                title: 'TID',
                CellComponent: ({ rowIndex, columnIndex, className, ...rest }) => {
                    const item = this.list[rowIndex];
@@ -196,7 +195,7 @@ const state = makeAutoObservable({
                }
            },
            {
                width: 100,
                width: 80,
                title: 'Priority',
                CellComponent: ({ rowIndex, columnIndex, className, ...rest }) => {
                    const item = this.list[rowIndex];
@@ -205,26 +204,37 @@ const state = makeAutoObservable({

                    return (
                        <div className={mergeClasses(classes.code, className)} {...rest}>
                            {LogPriority[item.priority]}
                            {AndroidLogPriority[item.priority]}
                        </div>
                    );
                }
            },
            {
                width: 300,
                flexGrow: 1,
                title: 'Payload',
                title: 'Tag',
                CellComponent: ({ rowIndex, columnIndex, className, ...rest }) => {
                    const item = this.list[rowIndex];
                    if (!item.payloadString) {
                        item.payloadString = decodeUtf8(item.payload);
                    }

                    const classes = useClasses();

                    return (
                        <div className={mergeClasses(classes.code, className)} {...rest}>
                            {item.payloadString}
                            {item.tag}
                        </div>
                    );
                }
            },
            {
                width: 300,
                flexGrow: 1,
                title: 'Message',
                CellComponent: ({ rowIndex, columnIndex, className, ...rest }) => {
                    const item = this.list[rowIndex];
                    const classes = useClasses();

                    return (
                        <div className={mergeClasses(classes.code, className)} {...rest}>
                            {item.message}
                        </div>
                    );
                }
+1 −1
Original line number Diff line number Diff line
@@ -16,7 +16,7 @@ export enum DemoModeSignalStrength {
    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+',
    'e', 'g', 'h', 'h+', 'lte', 'lte+', 'dis', 'not', 'null'] as const;

+39 −14
Original line number Diff line number Diff line
// cspell: ignore logcat

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`
// so instead of adding to core library, it's implemented here
@@ -19,7 +19,8 @@ export enum LogId {
    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,
    Default,
    Verbose,
@@ -56,21 +57,45 @@ export const LoggerEntry =

export type LoggerEntry = typeof LoggerEntry['TDeserializeResult'];

export interface LogMessage extends LoggerEntry {
    priority: LogPriority;
    payload: Uint8Array;
// https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/logprint.cpp;drc=bbe77d66e7bee8bd1f0bc7e5492b5376b0207ef6;bpv=0
export interface AndroidLogEntry extends LoggerEntry {
    priority: AndroidLogPriority;
    tag: string;
    message: string;
}

export async function deserializeLogMessage(stream: StructAsyncDeserializeStream): Promise<LogMessage> {
    const entry = await LoggerEntry.deserialize(stream);
function findTagEnd(payload: Uint8Array) {
    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) {
        await stream.read(entry.headerSize - LoggerEntry.size);
    }
    const priority = (await stream.read(1))[0] as LogPriority;
    const payload = await stream.read(entry.payloadSize - 1);
    (entry as any).priority = priority;
    (entry as any).payload = payload;
    return entry as LogMessage;
    let payload = await stream.read(entry.payloadSize);

    // https://cs.android.com/android/platform/superproject/+/master:system/logging/logcat/logcat.cpp;l=193-194;drc=bbe77d66e7bee8bd1f0bc7e5492b5376b0207ef6
    // TODO: payload for some log IDs are in binary format.
    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 {
@@ -158,7 +183,7 @@ export class Logcat extends AdbCommandBase {
        ]);
    }

    public binary(options?: LogcatOptions): ReadableStream<LogMessage> {
    public binary(options?: LogcatOptions): ReadableStream<AndroidLogEntry> {
        let bufferedStream: BufferedStream;
        return new ReadableStream({
            start: async () => {
@@ -175,7 +200,7 @@ export class Logcat extends AdbCommandBase {
            },
            async pull(controller) {
                try {
                    const entry = await deserializeLogMessage(bufferedStream);
                    const entry = await deserializeAndroidLogEntry(bufferedStream);
                    controller.enqueue(entry);
                } catch (e) {
                    if (e instanceof BufferedStreamEndedError) {