chore: integrate ESLint

This commit is contained in:
Simon Chan 2022-12-22 01:42:24 +08:00
parent 8bcd0ab913
commit aa033e4de6
No known key found for this signature in database
GPG key ID: A8B69F750B9BCEDD
177 changed files with 5604 additions and 3798 deletions

View file

@ -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);
})
);
}
}