mirror of
https://github.com/yume-chan/ya-webadb.git
synced 2025-10-05 10:49:24 +02:00
chore: integrate ESLint
This commit is contained in:
parent
8bcd0ab913
commit
aa033e4de6
177 changed files with 5604 additions and 3798 deletions
|
@ -1,8 +1,18 @@
|
|||
// cspell: ignore logcat
|
||||
|
||||
import { AdbCommandBase, AdbSubprocessNoneProtocol } from '@yume-chan/adb';
|
||||
import { BufferedTransformStream, DecodeUtf8Stream, SplitStringStream, WrapReadableStream, WritableStream, type ReadableStream } from '@yume-chan/stream-extra';
|
||||
import Struct, { decodeUtf8, StructAsyncDeserializeStream } from '@yume-chan/struct';
|
||||
import { AdbCommandBase, AdbSubprocessNoneProtocol } from "@yume-chan/adb";
|
||||
import {
|
||||
BufferedTransformStream,
|
||||
DecodeUtf8Stream,
|
||||
SplitStringStream,
|
||||
WrapReadableStream,
|
||||
WritableStream,
|
||||
type ReadableStream,
|
||||
} from "@yume-chan/stream-extra";
|
||||
import Struct, {
|
||||
decodeUtf8,
|
||||
type 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
|
||||
|
@ -34,17 +44,18 @@ export enum AndroidLogPriority {
|
|||
}
|
||||
|
||||
// https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/logprint.cpp;l=140;drc=8dbf3b2bb6b6d1652d9797e477b9abd03278bb79
|
||||
export const AndroidLogPriorityToCharacter: Record<AndroidLogPriority, string> = {
|
||||
[AndroidLogPriority.Unknown]: '?',
|
||||
[AndroidLogPriority.Default]: '?',
|
||||
[AndroidLogPriority.Verbose]: 'V',
|
||||
[AndroidLogPriority.Debug]: 'D',
|
||||
[AndroidLogPriority.Info]: 'I',
|
||||
[AndroidLogPriority.Warn]: 'W',
|
||||
[AndroidLogPriority.Error]: 'E',
|
||||
[AndroidLogPriority.Fatal]: 'F',
|
||||
[AndroidLogPriority.Silent]: 'S',
|
||||
};
|
||||
export const AndroidLogPriorityToCharacter: Record<AndroidLogPriority, string> =
|
||||
{
|
||||
[AndroidLogPriority.Unknown]: "?",
|
||||
[AndroidLogPriority.Default]: "?",
|
||||
[AndroidLogPriority.Verbose]: "V",
|
||||
[AndroidLogPriority.Debug]: "D",
|
||||
[AndroidLogPriority.Info]: "I",
|
||||
[AndroidLogPriority.Warn]: "W",
|
||||
[AndroidLogPriority.Error]: "E",
|
||||
[AndroidLogPriority.Fatal]: "F",
|
||||
[AndroidLogPriority.Silent]: "S",
|
||||
};
|
||||
|
||||
export enum LogcatFormat {
|
||||
Brief,
|
||||
|
@ -54,7 +65,7 @@ export enum LogcatFormat {
|
|||
Raw,
|
||||
Time,
|
||||
ThreadTime,
|
||||
Long
|
||||
Long,
|
||||
}
|
||||
|
||||
export interface LogcatFormatModifiers {
|
||||
|
@ -76,23 +87,25 @@ export interface LogcatOptions {
|
|||
const NANOSECONDS_PER_SECOND = BigInt(1e9);
|
||||
|
||||
// https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/include/log/log_read.h;l=39;drc=82b5738732161dbaafb2e2f25cce19cd26b9157d
|
||||
export const LoggerEntry =
|
||||
new Struct({ littleEndian: true })
|
||||
.uint16('payloadSize')
|
||||
.uint16('headerSize')
|
||||
.int32('pid')
|
||||
.uint32('tid')
|
||||
.uint32('second')
|
||||
.uint32('nanoseconds')
|
||||
.uint32('logId')
|
||||
.uint32('uid')
|
||||
.extra({
|
||||
get timestamp() {
|
||||
return BigInt(this.second) * NANOSECONDS_PER_SECOND + BigInt(this.nanoseconds);
|
||||
},
|
||||
});
|
||||
export const LoggerEntry = new Struct({ littleEndian: true })
|
||||
.uint16("payloadSize")
|
||||
.uint16("headerSize")
|
||||
.int32("pid")
|
||||
.uint32("tid")
|
||||
.uint32("second")
|
||||
.uint32("nanoseconds")
|
||||
.uint32("logId")
|
||||
.uint32("uid")
|
||||
.extra({
|
||||
get timestamp() {
|
||||
return (
|
||||
BigInt(this.second) * NANOSECONDS_PER_SECOND +
|
||||
BigInt(this.nanoseconds)
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export type LoggerEntry = typeof LoggerEntry['TDeserializeResult'];
|
||||
export type LoggerEntry = typeof LoggerEntry["TDeserializeResult"];
|
||||
|
||||
// https://cs.android.com/android/platform/superproject/+/master:system/logging/liblog/logprint.cpp;drc=bbe77d66e7bee8bd1f0bc7e5492b5376b0207ef6;bpv=0
|
||||
export interface AndroidLogEntry extends LoggerEntry {
|
||||
|
@ -107,24 +120,28 @@ export function formatAndroidLogEntry(
|
|||
format: LogcatFormat = LogcatFormat.Brief,
|
||||
modifier?: LogcatFormatModifiers
|
||||
) {
|
||||
const uid = modifier?.uid ? `${entry.uid.toString().padStart(5)}:` : '';
|
||||
const uid = modifier?.uid ? `${entry.uid.toString().padStart(5)}:` : "";
|
||||
|
||||
switch (format) {
|
||||
// TODO: implement other formats
|
||||
default:
|
||||
return `${AndroidLogPriorityToCharacter[entry.priority]}/${entry.tag.padEnd(8)}(${uid}${entry.pid.toString().padStart(5)}): ${entry.message}`;
|
||||
return `${
|
||||
AndroidLogPriorityToCharacter[entry.priority]
|
||||
}/${entry.tag.padEnd(8)}(${uid}${entry.pid
|
||||
.toString()
|
||||
.padStart(5)}): ${entry.message}`;
|
||||
}
|
||||
}
|
||||
|
||||
function findTagEnd(payload: Uint8Array) {
|
||||
for (const separator of [0, ' '.charCodeAt(0), ':'.charCodeAt(0)]) {
|
||||
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);
|
||||
const index = payload.findIndex((x) => x >= 0x7f);
|
||||
if (index !== -1) {
|
||||
return index;
|
||||
}
|
||||
|
@ -132,8 +149,12 @@ function findTagEnd(payload: Uint8Array) {
|
|||
return payload.length;
|
||||
}
|
||||
|
||||
export async function deserializeAndroidLogEntry(stream: StructAsyncDeserializeStream): Promise<AndroidLogEntry> {
|
||||
const entry = await LoggerEntry.deserialize(stream) as unknown as AndroidLogEntry;
|
||||
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);
|
||||
}
|
||||
|
@ -146,7 +167,10 @@ export async function deserializeAndroidLogEntry(stream: StructAsyncDeserializeS
|
|||
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)) : '';
|
||||
entry.message =
|
||||
tagEnd < payload.length - 1
|
||||
? decodeUtf8(payload.subarray(tagEnd + 1))
|
||||
: "";
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
@ -166,72 +190,91 @@ export class Logcat extends AdbCommandBase {
|
|||
|
||||
public static logNameToId(name: string): LogId {
|
||||
const key = name[0]!.toUpperCase() + name.substring(1);
|
||||
return (LogId as any)[key];
|
||||
return LogId[key as keyof typeof LogId];
|
||||
}
|
||||
|
||||
public static joinLogId(ids: LogId[]): string {
|
||||
return ids.map(id => Logcat.logIdToName(id)).join(',');
|
||||
return ids.map((id) => Logcat.logIdToName(id)).join(",");
|
||||
}
|
||||
|
||||
public static parseSize(value: number, multiplier: string): number {
|
||||
const MULTIPLIERS = ['', 'Ki', 'Mi', 'Gi'];
|
||||
const MULTIPLIERS = ["", "Ki", "Mi", "Gi"];
|
||||
return value * 1024 ** (MULTIPLIERS.indexOf(multiplier) || 0);
|
||||
}
|
||||
|
||||
// TODO: logcat: Support output format before Android 10
|
||||
// ref https://android-review.googlesource.com/c/platform/system/core/+/748128
|
||||
public static readonly LOG_SIZE_REGEX_10 = /(.*): ring buffer is (.*) (.*)B \((.*) (.*)B consumed\), max entry is (.*) B, max payload is (.*) B/;
|
||||
public static readonly LOG_SIZE_REGEX_10 =
|
||||
/(.*): ring buffer is (.*) (.*)B \((.*) (.*)B consumed\), max entry is (.*) B, max payload is (.*) B/;
|
||||
|
||||
// Android 11 added `readable` part
|
||||
// ref https://android-review.googlesource.com/c/platform/system/core/+/1390940
|
||||
public static readonly LOG_SIZE_REGEX_11 = /(.*): ring buffer is (.*) (.*)B \((.*) (.*)B consumed, (.*) (.*)B readable\), max entry is (.*) B, max payload is (.*) B/;
|
||||
public static readonly LOG_SIZE_REGEX_11 =
|
||||
/(.*): ring buffer is (.*) (.*)B \((.*) (.*)B consumed, (.*) (.*)B readable\), max entry is (.*) B, max payload is (.*) B/;
|
||||
|
||||
public async getLogSize(ids?: LogId[]): Promise<LogSize[]> {
|
||||
const { stdout } = await this.adb.subprocess.spawn([
|
||||
'logcat',
|
||||
'-g',
|
||||
...(ids ? ['-b', Logcat.joinLogId(ids)] : [])
|
||||
"logcat",
|
||||
"-g",
|
||||
...(ids ? ["-b", Logcat.joinLogId(ids)] : []),
|
||||
]);
|
||||
|
||||
const result: LogSize[] = [];
|
||||
await stdout
|
||||
.pipeThrough(new DecodeUtf8Stream())
|
||||
.pipeThrough(new SplitStringStream('\n'))
|
||||
.pipeTo(new WritableStream({
|
||||
write(chunk) {
|
||||
let match = chunk.match(Logcat.LOG_SIZE_REGEX_11);
|
||||
if (match) {
|
||||
result.push({
|
||||
id: Logcat.logNameToId(match[1]!),
|
||||
size: Logcat.parseSize(Number.parseInt(match[2]!, 10), match[3]!),
|
||||
readable: Logcat.parseSize(Number.parseInt(match[6]!, 10), match[7]!),
|
||||
consumed: Logcat.parseSize(Number.parseInt(match[4]!, 10), match[5]!),
|
||||
maxEntrySize: parseInt(match[8]!, 10),
|
||||
maxPayloadSize: parseInt(match[9]!, 10),
|
||||
});
|
||||
}
|
||||
.pipeThrough(new SplitStringStream("\n"))
|
||||
.pipeTo(
|
||||
new WritableStream({
|
||||
write(chunk) {
|
||||
let match = chunk.match(Logcat.LOG_SIZE_REGEX_11);
|
||||
if (match) {
|
||||
result.push({
|
||||
id: Logcat.logNameToId(match[1]!),
|
||||
size: Logcat.parseSize(
|
||||
Number.parseInt(match[2]!, 10),
|
||||
match[3]!
|
||||
),
|
||||
readable: Logcat.parseSize(
|
||||
Number.parseInt(match[6]!, 10),
|
||||
match[7]!
|
||||
),
|
||||
consumed: Logcat.parseSize(
|
||||
Number.parseInt(match[4]!, 10),
|
||||
match[5]!
|
||||
),
|
||||
maxEntrySize: parseInt(match[8]!, 10),
|
||||
maxPayloadSize: parseInt(match[9]!, 10),
|
||||
});
|
||||
}
|
||||
|
||||
match = chunk.match(Logcat.LOG_SIZE_REGEX_10);
|
||||
if (match) {
|
||||
result.push({
|
||||
id: Logcat.logNameToId(match[1]!),
|
||||
size: Logcat.parseSize(Number.parseInt(match[2]!, 10), match[3]!),
|
||||
consumed: Logcat.parseSize(Number.parseInt(match[4]!, 10), match[5]!),
|
||||
maxEntrySize: parseInt(match[6]!, 10),
|
||||
maxPayloadSize: parseInt(match[7]!, 10),
|
||||
});
|
||||
}
|
||||
},
|
||||
}));
|
||||
match = chunk.match(Logcat.LOG_SIZE_REGEX_10);
|
||||
if (match) {
|
||||
result.push({
|
||||
id: Logcat.logNameToId(match[1]!),
|
||||
size: Logcat.parseSize(
|
||||
Number.parseInt(match[2]!, 10),
|
||||
match[3]!
|
||||
),
|
||||
consumed: Logcat.parseSize(
|
||||
Number.parseInt(match[4]!, 10),
|
||||
match[5]!
|
||||
),
|
||||
maxEntrySize: parseInt(match[6]!, 10),
|
||||
maxPayloadSize: parseInt(match[7]!, 10),
|
||||
});
|
||||
}
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public async clear(ids?: LogId[]) {
|
||||
await this.adb.subprocess.spawnAndWait([
|
||||
'logcat',
|
||||
'-c',
|
||||
...(ids ? ['-b', Logcat.joinLogId(ids)] : []),
|
||||
"logcat",
|
||||
"-c",
|
||||
...(ids ? ["-b", Logcat.joinLogId(ids)] : []),
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -239,18 +282,25 @@ export class Logcat extends AdbCommandBase {
|
|||
return new WrapReadableStream(async () => {
|
||||
// TODO: make `spawn` return synchronously with streams pending
|
||||
// so it's easier to chain them.
|
||||
const { stdout } = await this.adb.subprocess.spawn([
|
||||
'logcat',
|
||||
'-B',
|
||||
...(options?.pid ? ['--pid', options.pid.toString()] : []),
|
||||
...(options?.ids ? ['-b', Logcat.joinLogId(options.ids)] : [])
|
||||
], {
|
||||
// PERF: None protocol is 150% faster then Shell protocol
|
||||
protocols: [AdbSubprocessNoneProtocol],
|
||||
});
|
||||
const { stdout } = await this.adb.subprocess.spawn(
|
||||
[
|
||||
"logcat",
|
||||
"-B",
|
||||
...(options?.pid ? ["--pid", options.pid.toString()] : []),
|
||||
...(options?.ids
|
||||
? ["-b", Logcat.joinLogId(options.ids)]
|
||||
: []),
|
||||
],
|
||||
{
|
||||
// PERF: None protocol is 150% faster then Shell protocol
|
||||
protocols: [AdbSubprocessNoneProtocol],
|
||||
}
|
||||
);
|
||||
return stdout;
|
||||
}).pipeThrough(new BufferedTransformStream(stream => {
|
||||
return deserializeAndroidLogEntry(stream);
|
||||
}));
|
||||
}).pipeThrough(
|
||||
new BufferedTransformStream((stream) => {
|
||||
return deserializeAndroidLogEntry(stream);
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue