247 lines
8.0 KiB
ObjectPascal
247 lines
8.0 KiB
ObjectPascal
unit GameDiagnosticsReport;
|
|||
|
|
|
||
|
|
{$mode objfpc}{$H+}{$codepage utf8}
|
||
|
|
|
||
|
|
interface
|
||
|
|
|
||
|
|
type
|
||
|
|
TDiagnosticStatus = (dsNotRun, dsPass, dsFail);
|
||
|
|
|
||
|
|
TDiagnosticStageId = (
|
||
|
|
dsiResourceExists,
|
||
|
|
dsiResourceOpen,
|
||
|
|
dsiCallerStreamLoad,
|
||
|
|
dsiStreamState,
|
||
|
|
dsiSeekReadConsistency
|
||
|
|
);
|
||
|
|
|
||
|
|
TOptionalString = record
|
||
|
|
HasValue: Boolean;
|
||
|
|
Value: string;
|
||
|
|
end;
|
||
|
|
|
||
|
|
TOptionalInt64 = record
|
||
|
|
HasValue: Boolean;
|
||
|
|
Value: Int64;
|
||
|
|
end;
|
||
|
|
|
||
|
|
TOptionalBoolean = record
|
||
|
|
HasValue: Boolean;
|
||
|
|
Value: Boolean;
|
||
|
|
end;
|
||
|
|
|
||
|
|
TOptionalDiagnosticStageId = record
|
||
|
|
HasValue: Boolean;
|
||
|
|
Value: TDiagnosticStageId;
|
||
|
|
end;
|
||
|
|
|
||
|
|
TDiagnosticError = record
|
||
|
|
Present: Boolean;
|
||
|
|
ClassName: TOptionalString;
|
||
|
|
MessageText: TOptionalString;
|
||
|
|
end;
|
||
|
|
|
||
|
|
TFileIODiagnosticStreamResult = record
|
||
|
|
Size: TOptionalInt64;
|
||
|
|
PositionAfterLoad: TOptionalInt64;
|
||
|
|
ReadOffset: TOptionalInt64;
|
||
|
|
RequestedReadCount: TOptionalInt64;
|
||
|
|
FirstReadCount: TOptionalInt64;
|
||
|
|
PositionAfterFirstRead: TOptionalInt64;
|
||
|
|
SeekBackPosition: TOptionalInt64;
|
||
|
|
SecondReadCount: TOptionalInt64;
|
||
|
|
PositionAfterSecondRead: TOptionalInt64;
|
||
|
|
RepeatEqual: TOptionalBoolean;
|
||
|
|
end;
|
||
|
|
|
||
|
|
TFileIODiagnosticResult = record
|
||
|
|
Status: TDiagnosticStatus;
|
||
|
|
FailureStage: TOptionalDiagnosticStageId;
|
||
|
|
Error: TDiagnosticError;
|
||
|
|
Stages: array[TDiagnosticStageId] of TDiagnosticStatus;
|
||
|
|
Stream: TFileIODiagnosticStreamResult;
|
||
|
|
end;
|
||
|
|
|
||
|
|
TDiagnosticApplicationMetadata = record
|
||
|
|
Name: string;
|
||
|
|
PackageName: string;
|
||
|
|
Version: string;
|
||
|
|
VersionCode: Int64;
|
||
|
|
BuildId: TOptionalString;
|
||
|
|
end;
|
||
|
|
|
||
|
|
TDiagnosticRuntimeMetadata = record
|
||
|
|
Platform: string;
|
||
|
|
OsVersion: TOptionalString;
|
||
|
|
DeviceModel: TOptionalString;
|
||
|
|
CpuArch: TOptionalString;
|
||
|
|
end;
|
||
|
|
|
||
|
|
TDiagnosticSelectedMapMetadata = record
|
||
|
|
RelativePath: string;
|
||
|
|
Url: string;
|
||
|
|
end;
|
||
|
|
|
||
|
|
TDiagnosticReportState = record
|
||
|
|
ApplicationInfo: TDiagnosticApplicationMetadata;
|
||
|
|
RuntimeInfo: TDiagnosticRuntimeMetadata;
|
||
|
|
SelectedMap: TDiagnosticSelectedMapMetadata;
|
||
|
|
OverallStatus: TDiagnosticStatus;
|
||
|
|
FileIO: TFileIODiagnosticResult;
|
||
|
|
end;
|
||
|
|
|
||
|
|
function CurrentDiagnosticReport: TDiagnosticReportState;
|
||
|
|
procedure ReplaceFileIODiagnosticResult(const AResult: TFileIODiagnosticResult);
|
||
|
|
function DiagnosticStatusToken(const AStatus: TDiagnosticStatus): string;
|
||
|
|
function DiagnosticStageIdToken(const AStage: TDiagnosticStageId): string;
|
||
|
|
function DiagnosticReportJsonSnapshot: string;
|
||
|
|
|
||
|
|
implementation
|
||
|
|
|
||
|
|
uses
|
||
|
|
fpjson, GameSelectedMap;
|
||
|
|
|
||
|
|
var
|
||
|
|
CurrentFileIO: TFileIODiagnosticResult;
|
||
|
|
|
||
|
|
function DiagnosticStatusToken(const AStatus: TDiagnosticStatus): string;
|
||
|
|
begin
|
||
|
|
case AStatus of
|
||
|
|
dsNotRun: Result := 'NOT_RUN';
|
||
|
|
dsPass: Result := 'PASS';
|
||
|
|
dsFail: Result := 'FAIL';
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
|
||
|
|
function DiagnosticStageIdToken(const AStage: TDiagnosticStageId): string;
|
||
|
|
begin
|
||
|
|
case AStage of
|
||
|
|
dsiResourceExists: Result := 'resource_exists';
|
||
|
|
dsiResourceOpen: Result := 'resource_open';
|
||
|
|
dsiCallerStreamLoad: Result := 'caller_stream_load';
|
||
|
|
dsiStreamState: Result := 'stream_state';
|
||
|
|
dsiSeekReadConsistency: Result := 'seek_read_consistency';
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
|
||
|
|
function CurrentDiagnosticReport: TDiagnosticReportState;
|
||
|
|
begin
|
||
|
|
Result.ApplicationInfo.Name := 'kam_mobile';
|
||
|
|
Result.ApplicationInfo.PackageName := 'org.kammobile.kamremake';
|
||
|
|
Result.ApplicationInfo.Version := '0.4.0';
|
||
|
|
Result.ApplicationInfo.VersionCode := 30;
|
||
|
|
Result.ApplicationInfo.BuildId.HasValue := False;
|
||
|
|
Result.RuntimeInfo.Platform := 'android';
|
||
|
|
Result.RuntimeInfo.OsVersion.HasValue := False;
|
||
|
|
Result.RuntimeInfo.DeviceModel.HasValue := False;
|
||
|
|
Result.RuntimeInfo.CpuArch.HasValue := False;
|
||
|
|
Result.SelectedMap.RelativePath := SelectedMapRelativePath;
|
||
|
|
Result.SelectedMap.Url := SelectedMapUrl;
|
||
|
|
Result.FileIO := CurrentFileIO;
|
||
|
|
Result.OverallStatus := Result.FileIO.Status;
|
||
|
|
end;
|
||
|
|
|
||
|
|
procedure ReplaceFileIODiagnosticResult(const AResult: TFileIODiagnosticResult);
|
||
|
|
begin
|
||
|
|
CurrentFileIO := AResult;
|
||
|
|
end;
|
||
|
|
|
||
|
|
function OptionalStringJson(const AValue: TOptionalString): TJSONData;
|
||
|
|
begin
|
||
|
|
if AValue.HasValue then
|
||
|
|
Result := TJSONString.Create(AValue.Value)
|
||
|
|
else
|
||
|
|
Result := TJSONNull.Create;
|
||
|
|
end;
|
||
|
|
|
||
|
|
function OptionalInt64Json(const AValue: TOptionalInt64): TJSONData;
|
||
|
|
begin
|
||
|
|
if AValue.HasValue then
|
||
|
|
Result := TJSONIntegerNumber.Create(AValue.Value)
|
||
|
|
else
|
||
|
|
Result := TJSONNull.Create;
|
||
|
|
end;
|
||
|
|
|
||
|
|
function OptionalBooleanJson(const AValue: TOptionalBoolean): TJSONData;
|
||
|
|
begin
|
||
|
|
if AValue.HasValue then
|
||
|
|
Result := TJSONBoolean.Create(AValue.Value)
|
||
|
|
else
|
||
|
|
Result := TJSONNull.Create;
|
||
|
|
end;
|
||
|
|
|
||
|
|
function DiagnosticReportJsonSnapshot: string;
|
||
|
|
var
|
||
|
|
State: TDiagnosticReportState;
|
||
|
|
Root, ObjectValue, Action, Stage, Stream: TJSONObject;
|
||
|
|
Actions, Stages: TJSONArray;
|
||
|
|
StageId: TDiagnosticStageId;
|
||
|
|
begin
|
||
|
|
State := CurrentDiagnosticReport;
|
||
|
|
Root := TJSONObject.Create;
|
||
|
|
try
|
||
|
|
Root.Add('schema', 'kam-mobile-diagnostics/1');
|
||
|
|
ObjectValue := TJSONObject.Create;
|
||
|
|
ObjectValue.Add('name', State.ApplicationInfo.Name);
|
||
|
|
ObjectValue.Add('package', State.ApplicationInfo.PackageName);
|
||
|
|
ObjectValue.Add('version', State.ApplicationInfo.Version);
|
||
|
|
ObjectValue.Add('version_code', State.ApplicationInfo.VersionCode);
|
||
|
|
ObjectValue.Add('build_id', OptionalStringJson(State.ApplicationInfo.BuildId));
|
||
|
|
Root.Add('application', ObjectValue);
|
||
|
|
ObjectValue := TJSONObject.Create;
|
||
|
|
ObjectValue.Add('platform', State.RuntimeInfo.Platform);
|
||
|
|
ObjectValue.Add('os_version', OptionalStringJson(State.RuntimeInfo.OsVersion));
|
||
|
|
ObjectValue.Add('device_model', OptionalStringJson(State.RuntimeInfo.DeviceModel));
|
||
|
|
ObjectValue.Add('cpu_arch', OptionalStringJson(State.RuntimeInfo.CpuArch));
|
||
|
|
Root.Add('runtime', ObjectValue);
|
||
|
|
ObjectValue := TJSONObject.Create;
|
||
|
|
ObjectValue.Add('relative_path', State.SelectedMap.RelativePath);
|
||
|
|
ObjectValue.Add('url', State.SelectedMap.Url);
|
||
|
|
Root.Add('selected_map', ObjectValue);
|
||
|
|
Root.Add('overall_status', DiagnosticStatusToken(State.OverallStatus));
|
||
|
|
Actions := TJSONArray.Create;
|
||
|
|
Action := TJSONObject.Create;
|
||
|
|
Action.Add('id', 'file_io');
|
||
|
|
Action.Add('status', DiagnosticStatusToken(State.FileIO.Status));
|
||
|
|
if State.FileIO.FailureStage.HasValue then
|
||
|
|
Action.Add('failure_stage', DiagnosticStageIdToken(State.FileIO.FailureStage.Value))
|
||
|
|
else
|
||
|
|
Action.Add('failure_stage', TJSONNull.Create);
|
||
|
|
if State.FileIO.Error.Present then
|
||
|
|
begin
|
||
|
|
ObjectValue := TJSONObject.Create;
|
||
|
|
ObjectValue.Add('class', OptionalStringJson(State.FileIO.Error.ClassName));
|
||
|
|
ObjectValue.Add('message', OptionalStringJson(State.FileIO.Error.MessageText));
|
||
|
|
Action.Add('error', ObjectValue);
|
||
|
|
end else
|
||
|
|
Action.Add('error', TJSONNull.Create);
|
||
|
|
Stages := TJSONArray.Create;
|
||
|
|
for StageId := Low(TDiagnosticStageId) to High(TDiagnosticStageId) do
|
||
|
|
begin
|
||
|
|
Stage := TJSONObject.Create;
|
||
|
|
Stage.Add('id', DiagnosticStageIdToken(StageId));
|
||
|
|
Stage.Add('status', DiagnosticStatusToken(State.FileIO.Stages[StageId]));
|
||
|
|
Stages.Add(Stage);
|
||
|
|
end;
|
||
|
|
Action.Add('stages', Stages);
|
||
|
|
Stream := TJSONObject.Create;
|
||
|
|
Stream.Add('size', OptionalInt64Json(State.FileIO.Stream.Size));
|
||
|
|
Stream.Add('position_after_load', OptionalInt64Json(State.FileIO.Stream.PositionAfterLoad));
|
||
|
|
Stream.Add('read_offset', OptionalInt64Json(State.FileIO.Stream.ReadOffset));
|
||
|
|
Stream.Add('requested_read_count', OptionalInt64Json(State.FileIO.Stream.RequestedReadCount));
|
||
|
|
Stream.Add('first_read_count', OptionalInt64Json(State.FileIO.Stream.FirstReadCount));
|
||
|
|
Stream.Add('position_after_first_read', OptionalInt64Json(State.FileIO.Stream.PositionAfterFirstRead));
|
||
|
|
Stream.Add('seek_back_position', OptionalInt64Json(State.FileIO.Stream.SeekBackPosition));
|
||
|
|
Stream.Add('second_read_count', OptionalInt64Json(State.FileIO.Stream.SecondReadCount));
|
||
|
|
Stream.Add('position_after_second_read', OptionalInt64Json(State.FileIO.Stream.PositionAfterSecondRead));
|
||
|
|
Stream.Add('repeat_equal', OptionalBooleanJson(State.FileIO.Stream.RepeatEqual));
|
||
|
|
Action.Add('stream', Stream);
|
||
|
|
Actions.Add(Action);
|
||
|
|
Root.Add('actions', Actions);
|
||
|
|
Result := Root.AsJSON;
|
||
|
|
finally
|
||
|
|
Root.Free;
|
||
|
|
end;
|
||
|
|
end;
|
||
|
|
|
||
|
|
end.
|