kopia lustrzana https://github.com/ryukoposting/Signal-Android
Trace database methods.
rodzic
42d61518b3
commit
831cd2f297
|
@ -128,7 +128,7 @@ android {
|
|||
buildConfigField "String", "ZKGROUP_SERVER_PUBLIC_PARAMS", "\"AMhf5ywVwITZMsff/eCyudZx9JDmkkkbV6PInzG4p8x3VqVJSFiMvnvlEKWuRob/1eaIetR31IYeAbm0NdOuHH8Qi+Rexi1wLlpzIo1gstHWBfZzy1+qHRV5A4TqPp15YzBPm0WSggW6PbSn+F4lf57VCnHF7p8SvzAA2ZZJPYJURt8X7bbg+H3i+PEjH9DXItNEqs2sNcug37xZQDLm7X0=\""
|
||||
buildConfigField "String[]", "LANGUAGES", "new String[]{\"" + autoResConfig().collect { s -> s.replace('-r', '_') }.join('", "') + '"}'
|
||||
buildConfigField "int", "CANONICAL_VERSION_CODE", "$canonicalVersionCode"
|
||||
buildConfigField "int", "TRACE_EVENT_MAX", "2000"
|
||||
buildConfigField "int", "TRACE_EVENT_MAX", "3500"
|
||||
|
||||
ndk {
|
||||
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
|
||||
|
|
|
@ -247,11 +247,11 @@ public class FlipperSqlCipherAdapter extends DatabaseDriver<FlipperSqlCipherAdap
|
|||
}
|
||||
|
||||
public @NonNull SQLiteDatabase getReadable() {
|
||||
return sqlCipherOpenHelper.getReadableDatabase();
|
||||
return sqlCipherOpenHelper.getReadableDatabase().getSqlCipherDatabase();
|
||||
}
|
||||
|
||||
public @NonNull SQLiteDatabase getWritable() {
|
||||
return sqlCipherOpenHelper.getWritableDatabase();
|
||||
return sqlCipherOpenHelper.getWritableDatabase().getSqlCipherDatabase();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,193 +0,0 @@
|
|||
package org.thoughtcrime.securesms.tracing;
|
||||
|
||||
import android.os.SystemClock;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
import org.thoughtcrime.securesms.BuildConfig;
|
||||
import org.thoughtcrime.securesms.trace.TraceProtos;
|
||||
import org.thoughtcrime.securesms.trace.TraceProtos.Trace;
|
||||
import org.thoughtcrime.securesms.trace.TraceProtos.TracePacket;
|
||||
import org.thoughtcrime.securesms.trace.TraceProtos.TrackDescriptor;
|
||||
import org.thoughtcrime.securesms.trace.TraceProtos.TrackEvent;
|
||||
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* A class to create Perfetto-compatible traces. Currently keeps the entire trace in memory to
|
||||
* avoid weirdness with synchronizing to disk.
|
||||
*
|
||||
* Some general info on how the Perfetto format works:
|
||||
* - The file format is just a Trace proto (see Trace.proto)
|
||||
* - The Trace proto is just a series of TracePackets
|
||||
* - TracePackets can describe:
|
||||
* - Threads
|
||||
* - Start of a method
|
||||
* - End of a method
|
||||
* - (And a bunch of other stuff that's not relevant to use at this point)
|
||||
*
|
||||
* We keep a circular buffer of TracePackets for method calls, and we keep a separate list of
|
||||
* TracePackets for threads so we don't lose any of those.
|
||||
*
|
||||
* Serializing is just a matter of throwing all the TracePackets we have into a proto.
|
||||
*
|
||||
* Note: This class aims to be largely-thread-safe, but prioritizes speed and memory efficiency
|
||||
* above all else. These methods are going to be called very quickly from every thread imaginable,
|
||||
* and we want to create as little overhead as possible. The idea being that it's ok if we don't,
|
||||
* for example, keep a perfect circular buffer size if it allows us to reduce overhead. The only
|
||||
* cost of screwing up would be dropping a trace packet or something, which, while sad, won't affect
|
||||
* how the app functions.
|
||||
*/
|
||||
public final class TracerImpl implements Tracer {
|
||||
|
||||
private static final int TRUSTED_SEQUENCE_ID = 1;
|
||||
private static final byte[] SYNCHRONIZATION_MARKER = UuidUtil.toByteArray(UUID.fromString("82477a76-b28d-42ba-81dc-33326d57a079"));
|
||||
|
||||
private final Clock clock;
|
||||
private final Map<Long, TracePacket> threadPackets;
|
||||
private final Queue<TracePacket> eventPackets;
|
||||
private final AtomicInteger eventCount;
|
||||
|
||||
TracerImpl() {
|
||||
this.clock = SystemClock::elapsedRealtimeNanos;
|
||||
this.threadPackets = new ConcurrentHashMap<>();
|
||||
this.eventPackets = new ConcurrentLinkedQueue<>();
|
||||
this.eventCount = new AtomicInteger(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(@NonNull String methodName) {
|
||||
long time = clock.getTimeNanos();
|
||||
Thread currentThread = Thread.currentThread();
|
||||
|
||||
if (!threadPackets.containsKey(currentThread.getId())) {
|
||||
threadPackets.put(currentThread.getId(), forThread(currentThread));
|
||||
}
|
||||
|
||||
addPacket(forMethodStart(methodName, time, currentThread.getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(@NonNull String methodName, @NonNull String key, @NonNull String value) {
|
||||
long time = clock.getTimeNanos();
|
||||
Thread currentThread = Thread.currentThread();
|
||||
|
||||
if (!threadPackets.containsKey(currentThread.getId())) {
|
||||
threadPackets.put(currentThread.getId(), forThread(currentThread));
|
||||
}
|
||||
|
||||
addPacket(forMethodStart(methodName, time, currentThread.getId(), key, value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end(@NonNull String methodName) {
|
||||
addPacket(forMethodEnd(methodName, clock.getTimeNanos(), Thread.currentThread().getId()));
|
||||
}
|
||||
|
||||
public @NonNull byte[] serialize() {
|
||||
Trace.Builder trace = Trace.newBuilder();
|
||||
|
||||
for (TracePacket thread : threadPackets.values()) {
|
||||
trace.addPacket(thread);
|
||||
}
|
||||
|
||||
for (TracePacket event : eventPackets) {
|
||||
trace.addPacket(event);
|
||||
}
|
||||
|
||||
trace.addPacket(forSynchronization(clock.getTimeNanos()));
|
||||
|
||||
return trace.build().toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to add a packet to our list while keeping the size of our circular buffer in-check.
|
||||
* The tracking of the event count is not perfectly thread-safe, but doing it in a thread-safe
|
||||
* way would likely involve adding a lock, which we really don't want to do, since it'll add
|
||||
* unnecessary overhead.
|
||||
*
|
||||
* Note that we keep track of the event count separately because
|
||||
* {@link ConcurrentLinkedQueue#size()} is NOT a constant-time operation.
|
||||
*/
|
||||
private void addPacket(@NonNull TracePacket packet) {
|
||||
eventPackets.add(packet);
|
||||
|
||||
int size = eventCount.incrementAndGet();
|
||||
|
||||
for (int i = size; i > BuildConfig.TRACE_EVENT_MAX; i--) {
|
||||
eventPackets.poll();
|
||||
eventCount.decrementAndGet();
|
||||
}
|
||||
}
|
||||
|
||||
private static TracePacket forThread(@NonNull Thread thread) {
|
||||
return TracePacket.newBuilder()
|
||||
.setTrustedPacketSequenceId(TRUSTED_SEQUENCE_ID)
|
||||
.setTrackDescriptor(TrackDescriptor.newBuilder()
|
||||
.setUuid(thread.getId())
|
||||
.setName(thread.getName()))
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
private static TracePacket forMethodStart(@NonNull String name, long time, long threadId) {
|
||||
return TracePacket.newBuilder()
|
||||
.setTrustedPacketSequenceId(TRUSTED_SEQUENCE_ID)
|
||||
.setTimestamp(time)
|
||||
.setTrackEvent(TrackEvent.newBuilder()
|
||||
.setTrackUuid(threadId)
|
||||
.setName(name)
|
||||
.setType(TrackEvent.Type.TYPE_SLICE_BEGIN))
|
||||
.build();
|
||||
}
|
||||
|
||||
private static TracePacket forMethodStart(@NonNull String name, long time, long threadId, @NonNull String key, @NonNull String value) {
|
||||
return TracePacket.newBuilder()
|
||||
.setTrustedPacketSequenceId(TRUSTED_SEQUENCE_ID)
|
||||
.setTimestamp(time)
|
||||
.setTrackEvent(TrackEvent.newBuilder()
|
||||
.setTrackUuid(threadId)
|
||||
.setName(name)
|
||||
.setType(TrackEvent.Type.TYPE_SLICE_BEGIN)
|
||||
.addDebugAnnotations(TraceProtos.DebugAnnotation.newBuilder()
|
||||
.setName(key)
|
||||
.setStringValue(value)
|
||||
.build()))
|
||||
.build();
|
||||
}
|
||||
|
||||
private static TracePacket forMethodEnd(@NonNull String name, long time, long threadId) {
|
||||
return TracePacket.newBuilder()
|
||||
.setTrustedPacketSequenceId(TRUSTED_SEQUENCE_ID)
|
||||
.setTimestamp(time)
|
||||
.setTrackEvent(TrackEvent.newBuilder()
|
||||
.setTrackUuid(threadId)
|
||||
.setName(name)
|
||||
.setType(TrackEvent.Type.TYPE_SLICE_END))
|
||||
.build();
|
||||
}
|
||||
|
||||
private static TracePacket forSynchronization(long time) {
|
||||
return TracePacket.newBuilder()
|
||||
.setTrustedPacketSequenceId(TRUSTED_SEQUENCE_ID)
|
||||
.setTimestamp(time)
|
||||
.setSynchronizationMarker(ByteString.copyFrom(SYNCHRONIZATION_MARKER))
|
||||
.build();
|
||||
}
|
||||
|
||||
private interface Clock {
|
||||
long getTimeNanos();
|
||||
}
|
||||
}
|
|
@ -34,8 +34,6 @@ import com.bumptech.glide.Glide;
|
|||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||
|
|
|
@ -171,7 +171,7 @@ public class DatabaseFactory {
|
|||
}
|
||||
|
||||
public static SQLiteDatabase getBackupDatabase(Context context) {
|
||||
return getInstance(context).databaseHelper.getReadableDatabase();
|
||||
return getInstance(context).databaseHelper.getReadableDatabase().getSqlCipherDatabase();
|
||||
}
|
||||
|
||||
public static void upgradeRestored(Context context, SQLiteDatabase database){
|
||||
|
@ -241,7 +241,7 @@ public class DatabaseFactory {
|
|||
|
||||
SQLCipherMigrationHelper.migrateCiphertext(context, masterSecret,
|
||||
legacyOpenHelper.getWritableDatabase(),
|
||||
databaseHelper.getWritableDatabase(),
|
||||
databaseHelper.getWritableDatabase().getSqlCipherDatabase(),
|
||||
listener);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,8 +8,6 @@ import android.net.Uri;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
import org.thoughtcrime.securesms.tracing.Trace;
|
||||
|
|
|
@ -13,8 +13,6 @@ import androidx.annotation.WorkerThread;
|
|||
import com.annimon.stream.Stream;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
|
||||
import org.signal.storageservice.protos.groups.AccessControl;
|
||||
import org.signal.storageservice.protos.groups.Member;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
||||
|
|
|
@ -7,8 +7,6 @@ import android.database.Cursor;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.tracing.Trace;
|
||||
|
|
|
@ -23,8 +23,6 @@ import android.database.Cursor;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
import org.thoughtcrime.securesms.database.identity.IdentityRecordList;
|
||||
|
|
|
@ -7,8 +7,6 @@ import androidx.annotation.NonNull;
|
|||
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
import org.thoughtcrime.securesms.jobmanager.persistence.ConstraintSpec;
|
||||
import org.thoughtcrime.securesms.jobmanager.persistence.DependencySpec;
|
||||
|
|
|
@ -6,8 +6,6 @@ import android.database.Cursor;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
import org.thoughtcrime.securesms.keyvalue.KeyValueDataSet;
|
||||
import org.thoughtcrime.securesms.tracing.Trace;
|
||||
|
|
|
@ -7,8 +7,6 @@ import android.database.Cursor;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
|
||||
import org.thoughtcrime.securesms.attachments.DatabaseAttachment;
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
|
|
|
@ -6,8 +6,6 @@ import android.database.Cursor;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
import org.thoughtcrime.securesms.database.model.MegaphoneRecord;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
|
|
|
@ -10,8 +10,6 @@ import androidx.annotation.Nullable;
|
|||
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
import org.thoughtcrime.securesms.database.model.Mention;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
|
|
|
@ -12,7 +12,6 @@ import com.annimon.stream.Stream;
|
|||
import com.google.android.mms.pdu_alt.NotificationInd;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
import net.sqlcipher.database.SQLiteStatement;
|
||||
|
||||
import org.thoughtcrime.securesms.database.documents.Document;
|
||||
|
|
|
@ -29,7 +29,6 @@ import com.annimon.stream.Stream;
|
|||
import com.google.android.mms.pdu_alt.NotificationInd;
|
||||
import com.google.android.mms.pdu_alt.PduHeaders;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
import net.sqlcipher.database.SQLiteStatement;
|
||||
|
||||
import org.json.JSONArray;
|
||||
|
|
|
@ -24,7 +24,6 @@ import androidx.annotation.Nullable;
|
|||
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
import net.sqlcipher.database.SQLiteQueryBuilder;
|
||||
|
||||
import org.thoughtcrime.securesms.database.MessageDatabase.SyncMessageId;
|
||||
|
|
|
@ -7,8 +7,6 @@ import android.database.Cursor;
|
|||
import androidx.annotation.Nullable;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
import org.whispersystems.libsignal.InvalidKeyException;
|
||||
|
|
|
@ -6,8 +6,6 @@ import android.database.Cursor;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.tracing.Trace;
|
||||
|
|
|
@ -12,7 +12,6 @@ import androidx.annotation.Nullable;
|
|||
import com.annimon.stream.Stream;
|
||||
|
||||
import net.sqlcipher.database.SQLiteConstraintException;
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
||||
import org.signal.zkgroup.groups.GroupMasterKey;
|
||||
|
@ -35,7 +34,6 @@ import org.thoughtcrime.securesms.jobs.RetrieveProfileJob;
|
|||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
||||
import org.thoughtcrime.securesms.profiles.ProfileName;
|
||||
import org.thoughtcrime.securesms.recipients.LiveRecipient;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
|
||||
|
|
|
@ -6,7 +6,6 @@ import android.content.Context;
|
|||
import androidx.annotation.NonNull;
|
||||
|
||||
import net.sqlcipher.Cursor;
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
|
|
|
@ -0,0 +1,332 @@
|
|||
package org.thoughtcrime.securesms.database;
|
||||
|
||||
|
||||
import android.content.ContentValues;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.gms.vision.Tracker;
|
||||
|
||||
import net.sqlcipher.Cursor;
|
||||
import net.sqlcipher.SQLException;
|
||||
import net.sqlcipher.database.SQLiteQueryStats;
|
||||
import net.sqlcipher.database.SQLiteStatement;
|
||||
import net.sqlcipher.database.SQLiteTransactionListener;
|
||||
|
||||
import org.thoughtcrime.securesms.tracing.Tracer;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This is a wrapper around {@link net.sqlcipher.database.SQLiteDatabase}. There's difficulties
|
||||
* making a subclass, so instead we just match the interface. Callers should just need to change
|
||||
* their import statements.
|
||||
*/
|
||||
public class SQLiteDatabase {
|
||||
|
||||
public static final int CONFLICT_ROLLBACK = 1;
|
||||
public static final int CONFLICT_ABORT = 2;
|
||||
public static final int CONFLICT_FAIL = 3;
|
||||
public static final int CONFLICT_IGNORE = 4;
|
||||
public static final int CONFLICT_REPLACE = 5;
|
||||
public static final int CONFLICT_NONE = 0;
|
||||
|
||||
private static final String KEY_QUERY = "query";
|
||||
private static final String KEY_TABLE = "table";
|
||||
private static final String KEY_THREAD = "thread";
|
||||
private static final String NAME_LOCK = "LOCK";
|
||||
|
||||
private final net.sqlcipher.database.SQLiteDatabase wrapped;
|
||||
private final Tracer tracer;
|
||||
|
||||
public SQLiteDatabase(net.sqlcipher.database.SQLiteDatabase wrapped) {
|
||||
this.wrapped = wrapped;
|
||||
this.tracer = Tracer.getInstance();
|
||||
}
|
||||
|
||||
private void traceLockStart() {
|
||||
tracer.start(NAME_LOCK, Tracer.TrackId.DB_LOCK, KEY_THREAD, Thread.currentThread().getName());
|
||||
}
|
||||
|
||||
private void traceLockEnd() {
|
||||
tracer.end(NAME_LOCK, Tracer.TrackId.DB_LOCK);
|
||||
}
|
||||
|
||||
private void trace(String methodName, Runnable runnable) {
|
||||
tracer.start(methodName);
|
||||
runnable.run();
|
||||
tracer.end(methodName);
|
||||
}
|
||||
|
||||
private void traceSql(String methodName, String query, boolean locked, Runnable returnable) {
|
||||
if (locked) {
|
||||
traceLockStart();
|
||||
}
|
||||
|
||||
tracer.start(methodName, KEY_QUERY, query);
|
||||
returnable.run();
|
||||
tracer.end(methodName);
|
||||
|
||||
if (locked) {
|
||||
traceLockEnd();
|
||||
}
|
||||
}
|
||||
|
||||
private <E> E traceSql(String methodName, String query, boolean locked, Returnable<E> returnable) {
|
||||
return traceSql(methodName, null, query, locked, returnable);
|
||||
}
|
||||
|
||||
private <E> E traceSql(String methodName, String table, String query, boolean locked, Returnable<E> returnable) {
|
||||
if (locked) {
|
||||
traceLockStart();
|
||||
}
|
||||
|
||||
Map<String, String> params = new HashMap<>();
|
||||
if (query != null) {
|
||||
params.put(KEY_QUERY, query);
|
||||
}
|
||||
if (table != null) {
|
||||
params.put(KEY_TABLE, table);
|
||||
}
|
||||
|
||||
tracer.start(methodName, params);
|
||||
E result = returnable.run();
|
||||
tracer.end(methodName);
|
||||
|
||||
if (locked) {
|
||||
traceLockEnd();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public net.sqlcipher.database.SQLiteDatabase getSqlCipherDatabase() {
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
private interface Returnable<E> {
|
||||
E run();
|
||||
}
|
||||
|
||||
|
||||
// =======================================================
|
||||
// Traced
|
||||
// =======================================================
|
||||
|
||||
public void beginTransaction() {
|
||||
traceLockStart();
|
||||
trace("beginTransaction()", wrapped::beginTransaction);
|
||||
}
|
||||
|
||||
public void endTransaction() {
|
||||
trace("endTransaction()", wrapped::endTransaction);
|
||||
traceLockEnd();
|
||||
}
|
||||
|
||||
public void setTransactionSuccessful() {
|
||||
trace("setTransactionSuccessful()", wrapped::setTransactionSuccessful);
|
||||
}
|
||||
|
||||
public Cursor query(boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit) {
|
||||
return traceSql("query(9)", table, selection, false, () -> wrapped.query(distinct, table, columns, selection, selectionArgs, groupBy, having, orderBy, limit));
|
||||
}
|
||||
|
||||
public Cursor queryWithFactory(net.sqlcipher.database.SQLiteDatabase.CursorFactory cursorFactory, boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit) {
|
||||
return traceSql("queryWithFactory()", table, selection, false, () -> wrapped.queryWithFactory(cursorFactory, distinct, table, columns, selection, selectionArgs, groupBy, having, orderBy, limit));
|
||||
}
|
||||
|
||||
public Cursor query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy) {
|
||||
return traceSql("query(7)", table, selection, false, () -> wrapped.query(table, columns, selection, selectionArgs, groupBy, having, orderBy));
|
||||
}
|
||||
|
||||
public Cursor query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit) {
|
||||
return traceSql("query(8)", table, selection, false, () -> wrapped.query(table, columns, selection, selectionArgs, groupBy, having, orderBy, limit));
|
||||
}
|
||||
|
||||
public Cursor rawQuery(String sql, String[] selectionArgs) {
|
||||
return traceSql("rawQuery(2a)", sql, false, () -> wrapped.rawQuery(sql, selectionArgs));
|
||||
}
|
||||
|
||||
public Cursor rawQuery(String sql, Object[] args) {
|
||||
return traceSql("rawQuery(2b)", sql, false,() -> wrapped.rawQuery(sql, args));
|
||||
}
|
||||
|
||||
public Cursor rawQueryWithFactory(net.sqlcipher.database.SQLiteDatabase.CursorFactory cursorFactory, String sql, String[] selectionArgs, String editTable) {
|
||||
return traceSql("rawQueryWithFactory()", sql, false, () -> wrapped.rawQueryWithFactory(cursorFactory, sql, selectionArgs, editTable));
|
||||
}
|
||||
|
||||
public Cursor rawQuery(String sql, String[] selectionArgs, int initialRead, int maxRead) {
|
||||
return traceSql("rawQuery(4)", sql, false, () -> rawQuery(sql, selectionArgs, initialRead, maxRead));
|
||||
}
|
||||
|
||||
public long insert(String table, String nullColumnHack, ContentValues values) {
|
||||
return traceSql("insert()", table, null, true, () -> wrapped.insert(table, nullColumnHack, values));
|
||||
}
|
||||
|
||||
public long insertOrThrow(String table, String nullColumnHack, ContentValues values) throws SQLException {
|
||||
return traceSql("insertOrThrow()", table, null, true, () -> wrapped.insertOrThrow(table, nullColumnHack, values));
|
||||
}
|
||||
|
||||
public long replace(String table, String nullColumnHack, ContentValues initialValues) {
|
||||
return traceSql("replace()", table, null, true,() -> wrapped.replace(table, nullColumnHack, initialValues));
|
||||
}
|
||||
|
||||
public long replaceOrThrow(String table, String nullColumnHack, ContentValues initialValues) throws SQLException {
|
||||
return traceSql("replaceOrThrow()", table, null, true, () -> wrapped.replaceOrThrow(table, nullColumnHack, initialValues));
|
||||
}
|
||||
|
||||
public long insertWithOnConflict(String table, String nullColumnHack, ContentValues initialValues, int conflictAlgorithm) {
|
||||
return traceSql("insertWithOnConflict()", table, null, true, () -> wrapped.insertWithOnConflict(table, nullColumnHack, initialValues, conflictAlgorithm));
|
||||
}
|
||||
|
||||
public int delete(String table, String whereClause, String[] whereArgs) {
|
||||
return traceSql("delete()", table, whereClause, true, () -> wrapped.delete(table, whereClause, whereArgs));
|
||||
}
|
||||
|
||||
public int update(String table, ContentValues values, String whereClause, String[] whereArgs) {
|
||||
return traceSql("update()", table, whereClause, true, () -> wrapped.update(table, values, whereClause, whereArgs));
|
||||
}
|
||||
|
||||
public int updateWithOnConflict(String table, ContentValues values, String whereClause, String[] whereArgs, int conflictAlgorithm) {
|
||||
return traceSql("updateWithOnConflict()", table, whereClause, true, () -> wrapped.updateWithOnConflict(table, values, whereClause, whereArgs, conflictAlgorithm));
|
||||
}
|
||||
|
||||
public void execSQL(String sql) throws SQLException {
|
||||
traceSql("execSQL(1)", sql, true, () -> wrapped.execSQL(sql));
|
||||
}
|
||||
|
||||
public void rawExecSQL(String sql) {
|
||||
traceSql("rawExecSQL()", sql, true, () -> wrapped.rawExecSQL(sql));
|
||||
}
|
||||
|
||||
public void execSQL(String sql, Object[] bindArgs) throws SQLException {
|
||||
traceSql("execSQL(2)", sql, true, () -> wrapped.execSQL(sql, bindArgs));
|
||||
}
|
||||
|
||||
|
||||
// =======================================================
|
||||
// Ignored
|
||||
// =======================================================
|
||||
|
||||
public boolean enableWriteAheadLogging() {
|
||||
return wrapped.enableWriteAheadLogging();
|
||||
}
|
||||
|
||||
public void disableWriteAheadLogging() {
|
||||
wrapped.disableWriteAheadLogging();
|
||||
}
|
||||
|
||||
public boolean isWriteAheadLoggingEnabled() {
|
||||
return wrapped.isWriteAheadLoggingEnabled();
|
||||
}
|
||||
|
||||
public void setForeignKeyConstraintsEnabled(boolean enable) {
|
||||
wrapped.setForeignKeyConstraintsEnabled(enable);
|
||||
}
|
||||
|
||||
public void beginTransactionWithListener(SQLiteTransactionListener transactionListener) {
|
||||
wrapped.beginTransactionWithListener(transactionListener);
|
||||
}
|
||||
|
||||
public void beginTransactionNonExclusive() {
|
||||
wrapped.beginTransactionNonExclusive();
|
||||
}
|
||||
|
||||
public void beginTransactionWithListenerNonExclusive(SQLiteTransactionListener transactionListener) {
|
||||
wrapped.beginTransactionWithListenerNonExclusive(transactionListener);
|
||||
}
|
||||
|
||||
public boolean inTransaction() {
|
||||
return wrapped.inTransaction();
|
||||
}
|
||||
|
||||
public boolean isDbLockedByCurrentThread() {
|
||||
return wrapped.isDbLockedByCurrentThread();
|
||||
}
|
||||
|
||||
public boolean isDbLockedByOtherThreads() {
|
||||
return wrapped.isDbLockedByOtherThreads();
|
||||
}
|
||||
|
||||
public boolean yieldIfContendedSafely() {
|
||||
return wrapped.yieldIfContendedSafely();
|
||||
}
|
||||
|
||||
public boolean yieldIfContendedSafely(long sleepAfterYieldDelay) {
|
||||
return wrapped.yieldIfContendedSafely(sleepAfterYieldDelay);
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return wrapped.getVersion();
|
||||
}
|
||||
|
||||
public void setVersion(int version) {
|
||||
wrapped.setVersion(version);
|
||||
}
|
||||
|
||||
public long getMaximumSize() {
|
||||
return wrapped.getMaximumSize();
|
||||
}
|
||||
|
||||
public long setMaximumSize(long numBytes) {
|
||||
return wrapped.setMaximumSize(numBytes);
|
||||
}
|
||||
|
||||
public long getPageSize() {
|
||||
return wrapped.getPageSize();
|
||||
}
|
||||
|
||||
public void setPageSize(long numBytes) {
|
||||
wrapped.setPageSize(numBytes);
|
||||
}
|
||||
|
||||
public SQLiteStatement compileStatement(String sql) throws SQLException {
|
||||
return wrapped.compileStatement(sql);
|
||||
}
|
||||
|
||||
public SQLiteQueryStats getQueryStats(String sql, Object[] args) {
|
||||
return wrapped.getQueryStats(sql, args);
|
||||
}
|
||||
|
||||
public boolean isReadOnly() {
|
||||
return wrapped.isReadOnly();
|
||||
}
|
||||
|
||||
public boolean isOpen() {
|
||||
return wrapped.isOpen();
|
||||
}
|
||||
|
||||
public boolean needUpgrade(int newVersion) {
|
||||
return wrapped.needUpgrade(newVersion);
|
||||
}
|
||||
|
||||
public final String getPath() {
|
||||
return wrapped.getPath();
|
||||
}
|
||||
|
||||
public void setLocale(Locale locale) {
|
||||
wrapped.setLocale(locale);
|
||||
}
|
||||
|
||||
public boolean isInCompiledSqlCache(String sql) {
|
||||
return wrapped.isInCompiledSqlCache(sql);
|
||||
}
|
||||
|
||||
public void purgeFromCompiledSqlCache(String sql) {
|
||||
wrapped.purgeFromCompiledSqlCache(sql);
|
||||
}
|
||||
|
||||
public void resetCompiledSqlCache() {
|
||||
wrapped.resetCompiledSqlCache();
|
||||
}
|
||||
|
||||
public int getMaxSqlCacheSize() {
|
||||
return wrapped.getMaxSqlCacheSize();
|
||||
}
|
||||
|
||||
public void setMaxSqlCacheSize(int cacheSize) {
|
||||
wrapped.setMaxSqlCacheSize(cacheSize);
|
||||
}
|
||||
}
|
|
@ -8,7 +8,6 @@ import androidx.annotation.NonNull;
|
|||
import com.annimon.stream.Stream;
|
||||
|
||||
import net.sqlcipher.Cursor;
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
import org.thoughtcrime.securesms.tracing.Trace;
|
||||
|
|
|
@ -7,8 +7,6 @@ import android.database.Cursor;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
|
|
|
@ -7,8 +7,6 @@ import android.database.Cursor;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
import org.thoughtcrime.securesms.logging.Log;
|
||||
import org.thoughtcrime.securesms.tracing.Trace;
|
||||
|
|
|
@ -28,7 +28,6 @@ import androidx.annotation.Nullable;
|
|||
import com.annimon.stream.Stream;
|
||||
import com.google.android.mms.pdu_alt.NotificationInd;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
import net.sqlcipher.database.SQLiteStatement;
|
||||
|
||||
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch;
|
||||
|
|
|
@ -25,7 +25,6 @@ import androidx.annotation.Nullable;
|
|||
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
import net.sqlcipher.database.SQLiteStatement;
|
||||
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
|
|
|
@ -9,8 +9,6 @@ import android.util.Pair;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.thoughtcrime.securesms.crypto.AttachmentSecret;
|
||||
import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream;
|
||||
|
|
|
@ -7,8 +7,6 @@ import android.database.Cursor;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
import org.thoughtcrime.securesms.tracing.Trace;
|
||||
import org.thoughtcrime.securesms.util.Base64;
|
||||
|
|
|
@ -29,8 +29,6 @@ import androidx.annotation.Nullable;
|
|||
import com.annimon.stream.Stream;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
|
||||
import org.jsoup.helper.StringUtil;
|
||||
import org.signal.zkgroup.InvalidInputException;
|
||||
import org.signal.zkgroup.groups.GroupMasterKey;
|
||||
|
|
|
@ -1246,12 +1246,12 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
|||
Log.i(TAG, "Upgrade complete. Took " + (System.currentTimeMillis() - startTime) + " ms.");
|
||||
}
|
||||
|
||||
public SQLiteDatabase getReadableDatabase() {
|
||||
return getReadableDatabase(databaseSecret.asString());
|
||||
public org.thoughtcrime.securesms.database.SQLiteDatabase getReadableDatabase() {
|
||||
return new org.thoughtcrime.securesms.database.SQLiteDatabase(getReadableDatabase(databaseSecret.asString()));
|
||||
}
|
||||
|
||||
public SQLiteDatabase getWritableDatabase() {
|
||||
return getWritableDatabase(databaseSecret.asString());
|
||||
public org.thoughtcrime.securesms.database.SQLiteDatabase getWritableDatabase() {
|
||||
return new org.thoughtcrime.securesms.database.SQLiteDatabase(getWritableDatabase(databaseSecret.asString()));
|
||||
}
|
||||
|
||||
public void markCurrent(SQLiteDatabase db) {
|
||||
|
|
|
@ -62,9 +62,7 @@ public class SubmitDebugLogRepository {
|
|||
add(new LogSectionCapabilities());
|
||||
add(new LogSectionFeatureFlags());
|
||||
add(new LogSectionPermissions());
|
||||
if (Tracer.getInstance().isEnabled()) {
|
||||
add(new LogSectionTrace());
|
||||
}
|
||||
add(new LogSectionTrace());
|
||||
add(new LogSectionThreads());
|
||||
add(new LogSectionLogcat());
|
||||
add(new LogSectionLogger());
|
||||
|
|
|
@ -30,10 +30,7 @@ public class SubmitDebugLogViewModel extends ViewModel {
|
|||
this.repo = new SubmitDebugLogRepository();
|
||||
this.lines = new DefaultValueLiveData<>(Collections.emptyList());
|
||||
this.mode = new MutableLiveData<>();
|
||||
|
||||
if (Tracer.getInstance().isEnabled()) {
|
||||
this.trace = Tracer.getInstance().serialize();
|
||||
}
|
||||
this.trace = Tracer.getInstance().serialize();
|
||||
|
||||
repo.getLogLines(result -> {
|
||||
sourceLines = result;
|
||||
|
|
|
@ -1,45 +1,229 @@
|
|||
package org.thoughtcrime.securesms.tracing;
|
||||
|
||||
import android.os.SystemClock;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
import org.thoughtcrime.securesms.BuildConfig;
|
||||
import org.thoughtcrime.securesms.trace.TraceProtos;
|
||||
import org.thoughtcrime.securesms.trace.TraceProtos.Trace;
|
||||
import org.thoughtcrime.securesms.trace.TraceProtos.TracePacket;
|
||||
import org.thoughtcrime.securesms.trace.TraceProtos.TrackDescriptor;
|
||||
import org.thoughtcrime.securesms.trace.TraceProtos.TrackEvent;
|
||||
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* A class to create Perfetto-compatible traces.
|
||||
* A class to create Perfetto-compatible traces. Currently keeps the entire trace in memory to
|
||||
* avoid weirdness with synchronizing to disk.
|
||||
*
|
||||
* Some general info on how the Perfetto format works:
|
||||
* - The file format is just a Trace proto (see Trace.proto)
|
||||
* - The Trace proto is just a series of TracePackets
|
||||
* - TracePackets can describe:
|
||||
* - Threads
|
||||
* - Start of a method
|
||||
* - End of a method
|
||||
* - (And a bunch of other stuff that's not relevant to use at this point)
|
||||
*
|
||||
* We keep a circular buffer of TracePackets for method calls, and we keep a separate list of
|
||||
* TracePackets for threads so we don't lose any of those.
|
||||
*
|
||||
* Serializing is just a matter of throwing all the TracePackets we have into a proto.
|
||||
*
|
||||
* Note: This class aims to be largely-thread-safe, but prioritizes speed and memory efficiency
|
||||
* above all else. These methods are going to be called very quickly from every thread imaginable,
|
||||
* and we want to create as little overhead as possible. The idea being that it's ok if we don't,
|
||||
* for example, keep a perfect circular buffer size if it allows us to reduce overhead. The only
|
||||
* cost of screwing up would be dropping a trace packet or something, which, while sad, won't affect
|
||||
* how the app functions.
|
||||
*/
|
||||
public interface Tracer {
|
||||
public final class Tracer {
|
||||
|
||||
TracerImpl INSTANCE = new TracerImpl();
|
||||
public static final class TrackId {
|
||||
public static final long DB_LOCK = -8675309;
|
||||
|
||||
static @NonNull Tracer getInstance() {
|
||||
private static final String DB_LOCK_NAME = "Database Lock";
|
||||
}
|
||||
|
||||
private static final Tracer INSTANCE = new Tracer();
|
||||
|
||||
private static final int TRUSTED_SEQUENCE_ID = 1;
|
||||
private static final byte[] SYNCHRONIZATION_MARKER = UuidUtil.toByteArray(UUID.fromString("82477a76-b28d-42ba-81dc-33326d57a079"));
|
||||
private static final long SYNCHRONIZATION_INTERVAL = TimeUnit.SECONDS.toNanos(3);
|
||||
|
||||
private final Clock clock;
|
||||
private final Map<Long, TracePacket> threadPackets;
|
||||
private final Queue<TracePacket> eventPackets;
|
||||
private final AtomicInteger eventCount;
|
||||
|
||||
private long lastSyncTime;
|
||||
|
||||
Tracer() {
|
||||
this.clock = SystemClock::elapsedRealtimeNanos;
|
||||
this.threadPackets = new ConcurrentHashMap<>();
|
||||
this.eventPackets = new ConcurrentLinkedQueue<>();
|
||||
this.eventCount = new AtomicInteger(0);
|
||||
}
|
||||
|
||||
public static @NonNull Tracer getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if enabled, otherwise false.
|
||||
*/
|
||||
boolean isEnabled();
|
||||
public void start(@NonNull String methodName) {
|
||||
start(methodName, Thread.currentThread().getId(), null);
|
||||
}
|
||||
|
||||
public void start(@NonNull String methodName, long trackId) {
|
||||
start(methodName, trackId, null);
|
||||
}
|
||||
|
||||
public void start(@NonNull String methodName, @NonNull String key, @Nullable String value) {
|
||||
start(methodName, Thread.currentThread().getId(), key, value);
|
||||
}
|
||||
|
||||
public void start(@NonNull String methodName, long trackId, @NonNull String key, @Nullable String value) {
|
||||
start(methodName, trackId, Collections.singletonMap(key, value));
|
||||
}
|
||||
|
||||
public void start(@NonNull String methodName, @Nullable Map<String, String> values) {
|
||||
start(methodName, Thread.currentThread().getId(), values);
|
||||
}
|
||||
|
||||
public void start(@NonNull String methodName, long trackId, @Nullable Map<String, String> values) {
|
||||
long time = clock.getTimeNanos();
|
||||
|
||||
if (time - lastSyncTime > SYNCHRONIZATION_INTERVAL) {
|
||||
addPacket(forSynchronization(time));
|
||||
lastSyncTime = time;
|
||||
}
|
||||
|
||||
if (!threadPackets.containsKey(trackId)) {
|
||||
threadPackets.put(trackId, forTrackId(trackId));
|
||||
}
|
||||
|
||||
addPacket(forMethodStart(methodName, time, trackId, values));
|
||||
}
|
||||
|
||||
public void end(@NonNull String methodName) {
|
||||
addPacket(forMethodEnd(methodName, clock.getTimeNanos(), Thread.currentThread().getId()));
|
||||
}
|
||||
|
||||
public void end(@NonNull String methodName, long trackId) {
|
||||
addPacket(forMethodEnd(methodName, clock.getTimeNanos(), trackId));
|
||||
}
|
||||
|
||||
public @NonNull byte[] serialize() {
|
||||
Trace.Builder trace = Trace.newBuilder();
|
||||
|
||||
for (TracePacket thread : threadPackets.values()) {
|
||||
trace.addPacket(thread);
|
||||
}
|
||||
|
||||
for (TracePacket event : eventPackets) {
|
||||
trace.addPacket(event);
|
||||
}
|
||||
|
||||
trace.addPacket(forSynchronization(clock.getTimeNanos()));
|
||||
|
||||
return trace.build().toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the start of a method call. Always follow this with a call to {@link #end(String)}.
|
||||
*/
|
||||
void start(@NonNull String methodName);
|
||||
|
||||
/**
|
||||
* Marks the start of a method call. Always follow this with a call to {@link #end(String)}.
|
||||
* Attempts to add a packet to our list while keeping the size of our circular buffer in-check.
|
||||
* The tracking of the event count is not perfectly thread-safe, but doing it in a thread-safe
|
||||
* way would likely involve adding a lock, which we really don't want to do, since it'll add
|
||||
* unnecessary overhead.
|
||||
*
|
||||
* Includes the ability to pass a key-value pair that will be shown in the trace when you click
|
||||
* on the slice.
|
||||
* Note that we keep track of the event count separately because
|
||||
* {@link ConcurrentLinkedQueue#size()} is NOT a constant-time operation.
|
||||
*/
|
||||
void start(@NonNull String methodName, @NonNull String key, @NonNull String value);
|
||||
private void addPacket(@NonNull TracePacket packet) {
|
||||
eventPackets.add(packet);
|
||||
|
||||
/**
|
||||
* Marks the end of a method call.
|
||||
*/
|
||||
void end(@NonNull String methodName);
|
||||
int size = eventCount.incrementAndGet();
|
||||
|
||||
/**
|
||||
* Serializes the current state of the trace to a Perfetto-compatible byte array. Note that
|
||||
* there's no locking here, and therefore tracing will continue. We're just grabbing a best-effort
|
||||
* snapshot.
|
||||
*/
|
||||
@NonNull byte[] serialize();
|
||||
for (int i = size; i > BuildConfig.TRACE_EVENT_MAX; i--) {
|
||||
eventPackets.poll();
|
||||
eventCount.decrementAndGet();
|
||||
}
|
||||
}
|
||||
|
||||
private TracePacket forTrackId(long id) {
|
||||
if (id == TrackId.DB_LOCK) {
|
||||
return forTrack(id, TrackId.DB_LOCK_NAME);
|
||||
} else {
|
||||
Thread currentThread = Thread.currentThread();
|
||||
return forTrack(currentThread.getId(), currentThread.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private static TracePacket forTrack(long id, String name) {
|
||||
return TracePacket.newBuilder()
|
||||
.setTrustedPacketSequenceId(TRUSTED_SEQUENCE_ID)
|
||||
.setTrackDescriptor(TrackDescriptor.newBuilder()
|
||||
.setUuid(id)
|
||||
.setName(name))
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
private static TracePacket forMethodStart(@NonNull String name, long time, long threadId, @Nullable Map<String, String> values) {
|
||||
TrackEvent.Builder event = TrackEvent.newBuilder()
|
||||
.setTrackUuid(threadId)
|
||||
.setName(name)
|
||||
.setType(TrackEvent.Type.TYPE_SLICE_BEGIN);
|
||||
|
||||
if (values != null) {
|
||||
for (Map.Entry<String, String> entry : values.entrySet()) {
|
||||
event.addDebugAnnotations(debugAnnotation(entry.getKey(), entry.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
return TracePacket.newBuilder()
|
||||
.setTrustedPacketSequenceId(TRUSTED_SEQUENCE_ID)
|
||||
.setTimestamp(time)
|
||||
.setTrackEvent(event)
|
||||
.build();
|
||||
}
|
||||
|
||||
private static TraceProtos.DebugAnnotation debugAnnotation(@NonNull String key, @Nullable String value) {
|
||||
return TraceProtos.DebugAnnotation.newBuilder()
|
||||
.setName(key)
|
||||
.setStringValue(value != null ? value : "")
|
||||
.build();
|
||||
}
|
||||
|
||||
private static TracePacket forMethodEnd(@NonNull String name, long time, long threadId) {
|
||||
return TracePacket.newBuilder()
|
||||
.setTrustedPacketSequenceId(TRUSTED_SEQUENCE_ID)
|
||||
.setTimestamp(time)
|
||||
.setTrackEvent(TrackEvent.newBuilder()
|
||||
.setTrackUuid(threadId)
|
||||
.setName(name)
|
||||
.setType(TrackEvent.Type.TYPE_SLICE_END))
|
||||
.build();
|
||||
}
|
||||
|
||||
private static TracePacket forSynchronization(long time) {
|
||||
return TracePacket.newBuilder()
|
||||
.setTrustedPacketSequenceId(TRUSTED_SEQUENCE_ID)
|
||||
.setTimestamp(time)
|
||||
.setSynchronizationMarker(ByteString.copyFrom(SYNCHRONIZATION_MARKER))
|
||||
.build();
|
||||
}
|
||||
|
||||
private interface Clock {
|
||||
long getTimeNanos();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
package org.thoughtcrime.securesms.tracing;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* Dummy implementation.
|
||||
*/
|
||||
final class TracerImpl implements Tracer {
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(@NonNull String methodName) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(@NonNull String methodName, @NonNull String key, @NonNull String value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end(@NonNull String methodName) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull byte[] serialize() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package org.thoughtcrime.securesms.tracing;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* Dummy implementation.
|
||||
*/
|
||||
final class TracerImpl implements Tracer {
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(@NonNull String methodName) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(@NonNull String methodName, @NonNull String key, @NonNull String value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end(@NonNull String methodName) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull byte[] serialize() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
Ładowanie…
Reference in New Issue