Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 74 additions & 72 deletions android/src/main/java/com/instabug/flutter/modules/ApmApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -110,42 +110,40 @@ public void setAutoUITraceEnabled(@NonNull Boolean isEnabled) {
*/
@Override
public void startExecutionTrace(@NonNull String id, @NonNull String name, ApmPigeon.Result<String> result) {
ThreadManager.runOnBackground(
new Runnable() {
@Override
public void run() {
try {
ExecutionTrace trace = APM.startExecutionTrace(name);
if (trace != null) {
traces.put(id, trace);

ThreadManager.runOnMainThread(new Runnable() {
@Override
public void run() {
result.success(id);
}
});
} else {
ThreadManager.runOnMainThread(new Runnable() {
@Override
public void run() {
result.success(null);
}
});
ThreadManager.runOnBackground(new Runnable() {
@Override
public void run() {
try {
ExecutionTrace trace = APM.startExecutionTrace(name);
if (trace != null) {
traces.put(id, trace);

ThreadManager.runOnMainThread(new Runnable() {
@Override
public void run() {
result.success(id);
}
} catch (Exception e) {
e.printStackTrace();

ThreadManager.runOnMainThread(new Runnable() {
@Override
public void run() {
result.success(null);
}
});
}
});
} else {
ThreadManager.runOnMainThread(new Runnable() {
@Override
public void run() {
result.success(null);
}
});
}
} catch (Exception e) {
e.printStackTrace();

ThreadManager.runOnMainThread(new Runnable() {
@Override
public void run() {
result.success(null);
}
});
}
);
}
});
}

/**
Expand Down Expand Up @@ -356,13 +354,9 @@ public void networkLogAndroid(@NonNull Map<String, Object> data) {
}


APMCPNetworkLog.W3CExternalTraceAttributes w3cExternalTraceAttributes =
null;
APMCPNetworkLog.W3CExternalTraceAttributes w3cExternalTraceAttributes = null;
if (isW3cHeaderFound != null) {
w3cExternalTraceAttributes = new APMCPNetworkLog.W3CExternalTraceAttributes(
isW3cHeaderFound, partialId == null ? null : partialId.longValue(),
networkStartTimeInSeconds == null ? null : networkStartTimeInSeconds.longValue(),
w3CGeneratedHeader, w3CCaughtHeader
w3cExternalTraceAttributes = new APMCPNetworkLog.W3CExternalTraceAttributes(isW3cHeaderFound, partialId == null ? null : partialId.longValue(), networkStartTimeInSeconds == null ? null : networkStartTimeInSeconds.longValue(), w3CGeneratedHeader, w3CCaughtHeader

);
}
Expand Down Expand Up @@ -520,49 +514,57 @@ public void deviceRefreshRate(@NonNull ApmPigeon.Result<Double> result) {

@Override
public void endScreenRenderForAutoUiTrace(@NonNull Map<String, Object> data) {
final long traceId = ((Number) data.get("traceId")).longValue();
final long slowFramesTotalDuration = ((Number) data.get("slowFramesTotalDuration")).longValue();
final long frozenFramesTotalDuration = ((Number) data.get("frozenFramesTotalDuration")).longValue();
final long endTime = ((Number) data.get("endTime")).longValue();

// Don't cast directly to ArrayList<ArrayList<Long>> because the inner lists may actually be ArrayList<Integer>
// Instead, cast to List<List<Number>> and convert each value to long explicitly
List<List<Number>> rawFrames = (List<List<Number>>) data.get("frameData");
ArrayList<IBGFrameData> frames = new ArrayList<>();
if (rawFrames != null) {
for (List<Number> frameValues : rawFrames) {
// Defensive: check size and nulls
if (frameValues != null && frameValues.size() >= 2) {
long frameStart = frameValues.get(0).longValue();
long frameDuration = frameValues.get(1).longValue();
frames.add(new IBGFrameData(frameStart, frameDuration));
try {
final long traceId = ((Number) data.get("traceId")).longValue();
final long slowFramesTotalDuration = ((Number) data.get("slowFramesTotalDuration")).longValue();
final long frozenFramesTotalDuration = ((Number) data.get("frozenFramesTotalDuration")).longValue();
final long endTime = ((Number) data.get("endTime")).longValue();

// Don't cast directly to ArrayList<ArrayList<Long>> because the inner lists may actually be ArrayList<Integer>
// Instead, cast to List<List<Number>> and convert each value to long explicitly
List<List<Number>> rawFrames = (List<List<Number>>) data.get("frameData");
ArrayList<IBGFrameData> frames = new ArrayList<>();
if (rawFrames != null) {
for (List<Number> frameValues : rawFrames) {
// Defensive: check size and nulls
if (frameValues != null && frameValues.size() >= 2) {
long frameStart = frameValues.get(0).longValue();
long frameDuration = frameValues.get(1).longValue();
frames.add(new IBGFrameData(frameStart, frameDuration));
}
}
}
IBGScreenRenderingData screenRenderingData = new IBGScreenRenderingData(traceId, slowFramesTotalDuration, frozenFramesTotalDuration, frames);
InternalAPM._endAutoUiTraceWithScreenRendering(screenRenderingData, endTime);
} catch (Exception e) {
e.printStackTrace();
}
IBGScreenRenderingData screenRenderingData = new IBGScreenRenderingData(traceId, slowFramesTotalDuration, frozenFramesTotalDuration, frames);
InternalAPM._endAutoUiTraceWithScreenRendering(screenRenderingData, endTime);
}

@Override
public void endScreenRenderForCustomUiTrace(@NonNull Map<String, Object> data) {
final long traceId = ((Number) data.get("traceId")).longValue();
final long slowFramesTotalDuration = ((Number) data.get("slowFramesTotalDuration")).longValue();
final long frozenFramesTotalDuration = ((Number) data.get("frozenFramesTotalDuration")).longValue();

List<List<Number>> rawFrames = (List<List<Number>>) data.get("frameData");
ArrayList<IBGFrameData> frames = new ArrayList<>();
if (rawFrames != null) {
for (List<Number> frameValues : rawFrames) {
// Defensive: check size and nulls
if (frameValues != null && frameValues.size() >= 2) {
long frameStart = frameValues.get(0).longValue();
long frameDuration = frameValues.get(1).longValue();
frames.add(new IBGFrameData(frameStart, frameDuration));
try {
final long traceId = ((Number) data.get("traceId")).longValue();
final long slowFramesTotalDuration = ((Number) data.get("slowFramesTotalDuration")).longValue();
final long frozenFramesTotalDuration = ((Number) data.get("frozenFramesTotalDuration")).longValue();

List<List<Number>> rawFrames = (List<List<Number>>) data.get("frameData");
ArrayList<IBGFrameData> frames = new ArrayList<>();
if (rawFrames != null) {
for (List<Number> frameValues : rawFrames) {
// Defensive: check size and nulls
if (frameValues != null && frameValues.size() >= 2) {
long frameStart = frameValues.get(0).longValue();
long frameDuration = frameValues.get(1).longValue();
frames.add(new IBGFrameData(frameStart, frameDuration));
}
}
}
IBGScreenRenderingData screenRenderingData = new IBGScreenRenderingData(traceId, slowFramesTotalDuration, frozenFramesTotalDuration, frames);
InternalAPM._endCustomUiTraceWithScreenRenderingCP(screenRenderingData);
} catch (Exception e) {
e.printStackTrace();
}
IBGScreenRenderingData screenRenderingData = new IBGScreenRenderingData(traceId, slowFramesTotalDuration, frozenFramesTotalDuration, frames);
InternalAPM._endCustomUiTraceWithScreenRenderingCP(screenRenderingData);
}

}
24 changes: 21 additions & 3 deletions lib/src/utils/screen_rendering/instabug_screen_render_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class InstabugScreenRenderManager {
int _frozenFramesTotalDurationMs = 0;
bool _isTimingsListenerAttached = false;
bool screenRenderEnabled = false;
int? _epochOffset;

final List<InstabugFrameData> _delayedFrames = [];

Expand Down Expand Up @@ -95,17 +96,23 @@ class InstabugScreenRenderManager {

if (_isUiDelayed) {
_onDelayedFrameDetected(
frameTiming.timestampInMicroseconds(FramePhase.buildStart),
_getMicrosecondsSinceEpoch(
frameTiming.timestampInMicroseconds(FramePhase.buildStart),
),
_buildTime,
);
} else if (_isRasterDelayed) {
_onDelayedFrameDetected(
frameTiming.timestampInMicroseconds(FramePhase.rasterStart),
_getMicrosecondsSinceEpoch(
frameTiming.timestampInMicroseconds(FramePhase.rasterStart),
),
_rasterTime,
);
} else if (_isTotalTimeLarge) {
_onDelayedFrameDetected(
frameTiming.timestampInMicroseconds(FramePhase.vsyncStart),
_getMicrosecondsSinceEpoch(
frameTiming.timestampInMicroseconds(FramePhase.vsyncStart),
),
_totalTime,
);
}
Expand Down Expand Up @@ -243,6 +250,9 @@ class InstabugScreenRenderManager {
/// Initialize the static variables
Future<void> _initStaticValues() async {
_timingsCallback = (timings) {
// 1. Establish the offset on the first available timing.
_epochOffset ??= _getEpochOffset(timings.first);

for (final frameTiming in timings) {
analyzeFrameTiming(frameTiming);
}
Expand All @@ -253,6 +263,11 @@ class InstabugScreenRenderManager {
_screenRenderForCustomUiTrace = InstabugScreenRenderData(frameData: []);
}

int _getEpochOffset(FrameTiming firstPatchedFrameTiming) {
return DateTime.now().microsecondsSinceEpoch -
firstPatchedFrameTiming.timestampInMicroseconds(FramePhase.vsyncStart);
}

/// Add a frame observer by calling [WidgetsBinding.instance.addTimingsCallback]
void _initFrameTimings() {
if (_isTimingsListenerAttached) {
Expand Down Expand Up @@ -369,6 +384,9 @@ class InstabugScreenRenderManager {
);
}

int _getMicrosecondsSinceEpoch(int timeInMicroseconds) =>
timeInMicroseconds + (_epochOffset ?? 0);

/// --------------------------- testing helper methods ---------------------

@visibleForTesting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ class _FakeSingletonFlutterWindow_1 extends _i1.Fake
class _FakePlatformDispatcher_2 extends _i1.Fake
implements _i4.PlatformDispatcher {}

class _FakeHardwareKeyboard_3 extends _i1.Fake implements _i5.HardwareKeyboard {
}
class _FakeHardwareKeyboard_3 extends _i1.Fake
implements _i5.HardwareKeyboard {}

class _FakeKeyEventManager_4 extends _i1.Fake implements _i5.KeyEventManager {}

Expand Down