0

Add sensitive netlog tracing to Cronet

This CL makes it possible to gather unredacted netlogs in a Cronet
Perfetto trace. Previously, this could only be done with "heavily
redacted" netlogs.

Unredacted netlogs are highly privacy and security sensitive. For this
reason, Cronet will only trace unredacted netlogs if the user jumps
through a number of hoops:

 - They must be using a debug Android OS build, OR the app using Cronet
   must be marked as "debuggable" in its manifest;

 - They must explicitly enable the feature on their device using the
   `debug.cronet.trace_netlog` system property (adb shell setprop);

 - When the feature is enabled netlogs are sent to a different trace
   category that is disabled by default, which means the Perfetto trace
   config must be explicitly set up to capture unredacted netlogs.

Bug: 410018349
Change-Id: Ibbfd71b6638f8db93ca107b8fef3be74ee035818
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6523443
Reviewed-by: Kenichi Ishibashi <bashi@chromium.org>
Reviewed-by: Etienne Pierre-Doray <etiennep@chromium.org>
Reviewed-by: Stefano Duo <stefanoduo@google.com>
Reviewed-by: Antonio Sartori <antoniosartori@chromium.org>
Auto-Submit: Etienne Dechamps <edechamps@google.com>
Commit-Queue: Antonio Sartori <antoniosartori@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1461180}
This commit is contained in:
Etienne Dechamps
2025-05-15 23:21:12 -07:00
committed by Chromium LUCI CQ
parent 281af2bacd
commit 4a3ce8d948
15 changed files with 391 additions and 18 deletions

@ -154,7 +154,10 @@ PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE_WITH_ATTRS(
perfetto::Category("net.stream").SetDescription(
"Includes events related to creating HTTP streams to serve requests."),
perfetto::Category("network.scheduler"),
perfetto::Category("netlog").SetTags("navigation"),
perfetto::Category("netlog").SetTags("navigation").SetDescription(
"NetLog events and metadata. Describes the operation of the //net "
"network stack, e.g. HTTP requests, TLS, DNS, connections, sockets, "
"etc."),
perfetto::Category("offline_pages"),
perfetto::Category("omnibox"),
perfetto::Category("oobe"),
@ -310,6 +313,12 @@ PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE_WITH_ATTRS(
perfetto::Category(TRACE_DISABLED_BY_DEFAULT("mojom")),
perfetto::Category(TRACE_DISABLED_BY_DEFAULT("navigation")),
perfetto::Category(TRACE_DISABLED_BY_DEFAULT("net")),
perfetto::Category(TRACE_DISABLED_BY_DEFAULT("netlog.sensitive")).SetTags(
"navigation", "sensitive").SetDescription(
"NetLog events and metadata, including sensitive information such as "
"hostnames, URLs, HTTP headers and other identifiable information. "
"Describes the operation of the //net network stack, e.g. HTTP requests, "
"TLS, DNS, connections, sockets, etc."),
perfetto::Category(TRACE_DISABLED_BY_DEFAULT("network")),
perfetto::Category(TRACE_DISABLED_BY_DEFAULT("paint-worklet")),
perfetto::Category(TRACE_DISABLED_BY_DEFAULT("power")),

@ -403,6 +403,15 @@ android_library("cronet_stats_log_java") {
deps = [ "//third_party/androidx:androidx_annotation_annotation_java" ]
}
# CronetStatsLog uses `android.os.SystemProperties` which is not in the public
# SDK, hence this separate target pointed at the system SDK.
android_library("cronet_android_os_system_properties_java") {
sources = [ "java/src/org/chromium/net/impl/AndroidOsSystemProperties.java" ]
alternative_android_sdk_dep =
"//third_party/android_sdk:public_framework_system_java"
deps = [ "//third_party/androidx:androidx_annotation_annotation_java" ]
}
cronet_impl_common_java_srcjar_deps = [
":effective_connection_type_java",
":http_cache_type_java",
@ -547,16 +556,19 @@ cronet_impl_native_java_srcjar_deps = [
cronet_impl_native_java_deps_to_package = [
":cronet_urlconnection_impl_java",
":request_context_config_java_proto",
":cronet_android_os_system_properties_java",
"//base/version_info/android:version_constants_java",
"//base:base_java",
"//third_party/jni_zero:jni_zero_java",
"//net/android:net_java",
"//url:url_java",
"//net/log:net_log_capture_mode_java",
]
# cronet_impl_native_java.jar - native implementation of the Cronet engine.
android_library("cronet_impl_native_java") {
sources = [
"java/src/org/chromium/net/impl/AndroidOsBuild.java",
"java/src/org/chromium/net/impl/BidirectionalStreamNetworkException.java",
"java/src/org/chromium/net/impl/CronetBidirectionalStream.java",
"java/src/org/chromium/net/impl/CronetLibraryLoader.java",
@ -1299,6 +1311,7 @@ android_library("cronet_fake_javatests") {
# This must not contain buildInfo as `cronet_perf_tests` APK generates
# another buildInfo and both of them starts colliding showing buildError.
cronet_javatests_deps_to_package = [
":cronet_android_os_system_properties_java",
":cronet_common_javatests",
":cronet_test_apk_java",
":flags_java_proto",
@ -1319,6 +1332,7 @@ cronet_javatests_deps_to_package = [
"//net/android:embedded_test_server_aidl_java",
"//net/android:net_java",
"//net/android:net_java_test_support",
"//net/log:net_log_capture_mode_java",
"//url:url_java",
"//third_party/jni_zero:generate_jni_java",
"//third_party/jni_zero:jni_zero_java",

@ -182,6 +182,10 @@
-keepclassmembers class org.chromium.** extends com.google.protobuf.GeneratedMessageLite {
<fields>;
}
# Part of the Android System SDK; false positive when pointing ProGuard to the
# public SDK.
-dontwarn android.os.SystemProperties
# -------- Config Path: components/cronet/android/cronet_shared_proguard.cfg --------
# Proguard config for apps that depend on cronet_shared_java.jar (which should
# be all apps that depend on any part of Cronet)

@ -60,3 +60,7 @@
-keepclassmembers class org.chromium.** extends com.google.protobuf.GeneratedMessageLite {
<fields>;
}
# Part of the Android System SDK; false positive when pointing ProGuard to the
# public SDK.
-dontwarn android.os.SystemProperties

@ -45,6 +45,7 @@
#include "net/android/network_change_notifier_factory_android.h"
#include "net/base/network_change_notifier.h"
#include "net/log/net_log.h"
#include "net/log/net_log_capture_mode.h"
#include "net/log/trace_net_log_observer.h"
#include "net/proxy_resolution/configured_proxy_resolution_service.h"
#include "net/proxy_resolution/proxy_config_service_android.h"
@ -80,6 +81,8 @@ base::WaitableEvent g_init_thread_init_done(
base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
std::optional<net::NetLogCaptureMode> g_trace_net_log_capture_mode;
::org::chromium::net::httpflags::BaseFeatureOverrides GetBaseFeatureOverrides(
JNIEnv* env) {
const auto serializedProto =
@ -212,7 +215,8 @@ void CronetOnUnLoad(JavaVM* jvm, void* reserved) {
void JNI_CronetLibraryLoader_CronetInitOnInitThread(
JNIEnv* env,
jboolean updateNetworkStateFromNative) {
jboolean updateNetworkStateFromNative,
net::NetLogCaptureMode trace_net_log_capture_mode) {
// Initialize SingleThreadTaskExecutor for init thread.
DCHECK(!base::CurrentThread::IsSet());
DCHECK(!g_init_task_executor);
@ -221,10 +225,12 @@ void JNI_CronetLibraryLoader_CronetInitOnInitThread(
static base::NoDestructor<net::TraceNetLogObserver> trace_net_log_observer(
net::TraceNetLogObserver::Options{
// TODO: https://crbug.com/410018349 - make it possible to select a
// a different capture mode, if running in a debug scenario.
.capture_mode = net::NetLogCaptureMode::kHeavilyRedacted,
.capture_mode = trace_net_log_capture_mode,
.use_sensitive_category = trace_net_log_capture_mode !=
net::NetLogCaptureMode::kHeavilyRedacted,
});
CHECK(!g_trace_net_log_capture_mode.has_value());
g_trace_net_log_capture_mode = trace_net_log_capture_mode;
// Note we do this on the init thread, as opposed to a user thread, because
// this eventually calls
// base::trace_event::TraceLog::AddAsyncEnabledStateObserver(), which
@ -248,6 +254,11 @@ void JNI_CronetLibraryLoader_CronetInitOnInitThread(
g_init_thread_init_done.Signal();
}
net::NetLogCaptureMode
JNI_CronetLibraryLoader_GetTraceNetLogCaptureModeForTesting(JNIEnv* env) {
return g_trace_net_log_capture_mode.value();
}
ScopedJavaLocalRef<jstring> JNI_CronetLibraryLoader_GetCronetVersion(
JNIEnv* env) {
#if defined(ARCH_CPU_ARM64)

@ -38,6 +38,7 @@
//net/dns
//net/dns/public
//net/http
//net/log
//net/third_party/quiche
//net/third_party/uri_template
//net/traffic_annotation

@ -0,0 +1,42 @@
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.net.impl;
import android.os.Build;
import androidx.annotation.VisibleForTesting;
@VisibleForTesting
public final class AndroidOsBuild {
private static AndroidOsBuild sOverrideForTesting;
private final String mType;
public AndroidOsBuild(String type) {
mType = type;
}
public static AndroidOsBuild get() {
return sOverrideForTesting != null ? sOverrideForTesting : new AndroidOsBuild(Build.TYPE);
}
/** See {@link android.os.Build#TYPE} */
public String getType() {
return mType;
}
public static final class WithOverrideForTesting implements AutoCloseable {
public WithOverrideForTesting(AndroidOsBuild override) {
assert sOverrideForTesting == null;
sOverrideForTesting = override;
}
@Override
public void close() {
assert sOverrideForTesting != null;
sOverrideForTesting = null;
}
}
}

@ -0,0 +1,37 @@
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.net.impl;
import android.os.SystemProperties;
import androidx.annotation.VisibleForTesting;
import java.util.Map;
@VisibleForTesting
public final class AndroidOsSystemProperties {
private static Map<String, String> sOverridesForTesting;
/** See {@link android.os.SystemProperties#get} */
public static String get(String key, String def) {
if (sOverridesForTesting == null) return SystemProperties.get(key, def);
var overrideForTesting = sOverridesForTesting.get(key);
return overrideForTesting != null ? overrideForTesting : def;
}
public static final class WithOverridesForTesting implements AutoCloseable {
public WithOverridesForTesting(Map<String, String> overrides) {
assert sOverridesForTesting == null;
sOverridesForTesting = overrides;
}
@Override
public void close() {
assert sOverridesForTesting != null;
sOverridesForTesting = null;
}
}
}

@ -5,6 +5,7 @@
package org.chromium.net.impl;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
@ -15,6 +16,7 @@ import androidx.annotation.VisibleForTesting;
import org.jni_zero.CalledByNative;
import org.jni_zero.JNINamespace;
import org.jni_zero.JniType;
import org.jni_zero.NativeMethods;
import org.chromium.base.BuildInfo;
@ -23,6 +25,7 @@ import org.chromium.base.ContextUtils;
import org.chromium.base.Log;
import org.chromium.base.TraceEvent;
import org.chromium.base.metrics.ScopedSysTraceEvent;
import org.chromium.net.NetLogCaptureMode;
import org.chromium.net.NetworkChangeNotifier;
import org.chromium.net.RegistrationPolicyAlwaysRegister;
import org.chromium.net.httpflags.BaseFeature;
@ -53,6 +56,9 @@ public class CronetLibraryLoader {
private static final ConditionVariable sHttpFlagsLoaded = new ConditionVariable();
@VisibleForTesting
public static final String TRACE_NET_LOG_SYSTEM_PROPERTY_KEY = "debug.cronet.trace_netlog";
@VisibleForTesting
public static final String UPDATE_NETWORK_STATE_ONCE_ON_STARTUP_FLAG_NAME =
"Cronet_UpdateNetworkStateOnlyOnceOnStartup";
@ -208,6 +214,49 @@ public class CronetLibraryLoader {
return sInitThread.getLooper() == Looper.myLooper();
}
private static @NetLogCaptureMode int getTraceNetLogCaptureMode() {
@NetLogCaptureMode int traceNetLogCaptureMode = NetLogCaptureMode.HEAVILY_REDACTED;
var requestedTraceNetLogCaptureMode =
AndroidOsSystemProperties.get(
TRACE_NET_LOG_SYSTEM_PROPERTY_KEY, "heavily_redacted");
if (requestedTraceNetLogCaptureMode.equals("heavily_redacted")) {
traceNetLogCaptureMode = NetLogCaptureMode.HEAVILY_REDACTED;
} else if (requestedTraceNetLogCaptureMode.equals("on")) {
// Note DEFAULT is mapped to "on", not "default", to avoid confusion with regard to
// the default value of the system property.
traceNetLogCaptureMode = NetLogCaptureMode.DEFAULT;
} else if (requestedTraceNetLogCaptureMode.equals("include_sensitive")) {
traceNetLogCaptureMode = NetLogCaptureMode.INCLUDE_SENSITIVE;
} else if (requestedTraceNetLogCaptureMode.equals("everything")) {
traceNetLogCaptureMode = NetLogCaptureMode.EVERYTHING;
} else {
Log.w(
TAG,
"Unknown value for %s system property, ignoring: %s",
TRACE_NET_LOG_SYSTEM_PROPERTY_KEY,
requestedTraceNetLogCaptureMode);
}
if (traceNetLogCaptureMode > NetLogCaptureMode.HEAVILY_REDACTED) {
final var buildType = AndroidOsBuild.get().getType();
if (!buildType.equals("userdebug")
&& !buildType.equals("eng")
&& (ContextUtils.getApplicationContext().getApplicationInfo().flags
& ApplicationInfo.FLAG_DEBUGGABLE)
== 0) {
Log.w(
TAG,
"Ignoring requested Cronet trace netlog capture mode (%s=%s) because"
+ " neither the device nor app are debuggable",
TRACE_NET_LOG_SYSTEM_PROPERTY_KEY,
requestedTraceNetLogCaptureMode);
traceNetLogCaptureMode = NetLogCaptureMode.HEAVILY_REDACTED;
}
}
return traceNetLogCaptureMode;
}
/**
* Runs Cronet initialization tasks on the init thread. Ensures that HTTP flags are loaded, the
* NetworkChangeNotifier is initialzied and the init thread native MessageLoop is initialized.
@ -257,6 +306,8 @@ public class CronetLibraryLoader {
new RegistrationPolicyAlwaysRegister(),
/* forceUpdateNetworkState= */ !updateNetworkStateOnce);
final var traceNetLogCaptureMode = getTraceNetLogCaptureMode();
try (var libLoadTraceEvent =
ScopedSysTraceEvent.scoped(
"CronetLibraryLoader#initializeOnInitThread waiting on library load")) {
@ -272,11 +323,16 @@ public class CronetLibraryLoader {
// NetworkChangeNotifierAndroid is created, so as to avoid receiving
// the undesired initial network change observer notification, which
// will cause active requests to fail with ERR_NETWORK_CHANGED.
CronetLibraryLoaderJni.get().cronetInitOnInitThread(!updateNetworkStateOnce);
CronetLibraryLoaderJni.get()
.cronetInitOnInitThread(!updateNetworkStateOnce, traceNetLogCaptureMode);
}
}
}
public static @NetLogCaptureMode int getTraceNetLogCaptureModeForTesting() {
return CronetLibraryLoaderJni.get().getTraceNetLogCaptureModeForTesting(); // IN-TEST
}
/** Run {@code r} on the initialization thread. */
public static void postToInitThread(Runnable r) {
if (onInitThread()) {
@ -349,7 +405,13 @@ public class CronetLibraryLoader {
// Native methods are implemented in cronet_library_loader.cc.
void nativeInit(boolean initializePerfetto);
void cronetInitOnInitThread(boolean updateNetworkStateFromNative);
void cronetInitOnInitThread(
boolean updateNetworkStateFromNative,
@NetLogCaptureMode @JniType("net::NetLogCaptureMode") int traceNetLogCaptureMode);
@NetLogCaptureMode
@JniType("net::NetLogCaptureMode")
int getTraceNetLogCaptureModeForTesting(); // IN-TEST
String getCronetVersion();

@ -13,6 +13,9 @@ import static org.chromium.net.CronetEngine.Builder.HTTP_CACHE_IN_MEMORY;
import static org.chromium.net.CronetTestRule.getTestStorage;
import static org.chromium.net.truth.UrlResponseInfoSubject.assertThat;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.ApplicationInfo;
import android.net.Network;
import android.os.Build;
import android.os.Bundle;
@ -49,6 +52,8 @@ import org.chromium.net.TestUrlRequestCallback.ResponseStep;
import org.chromium.net.httpflags.BaseFeature;
import org.chromium.net.httpflags.FlagValue;
import org.chromium.net.httpflags.HttpFlagsLoader;
import org.chromium.net.impl.AndroidOsBuild;
import org.chromium.net.impl.AndroidOsSystemProperties;
import org.chromium.net.impl.CronetEngineBuilderImpl;
import org.chromium.net.impl.CronetExceptionImpl;
import org.chromium.net.impl.CronetLibraryLoader;
@ -65,6 +70,7 @@ import java.io.File;
import java.io.FileReader;
import java.net.URL;
import java.util.Arrays;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
@ -412,6 +418,144 @@ public class CronetUrlRequestContextTest {
runRequestWhileExpectingLog(marker, /* shouldBeLogged= */ false);
}
@NetLogCaptureMode
int getTraceNetLogCaptureMode(
String traceNetLogSystemPropertyValue, String buildType, boolean appIsDebuggable) {
mTestRule
.getTestFramework()
.interceptContext(
new ContextInterceptor() {
@Override
public Context interceptContext(Context context) {
return new ContextWrapper(context) {
@Override
public ApplicationInfo getApplicationInfo() {
var applicationInfo =
new ApplicationInfo(context.getApplicationInfo());
applicationInfo.flags &= ~ApplicationInfo.FLAG_DEBUGGABLE;
if (appIsDebuggable) {
applicationInfo.flags |=
ApplicationInfo.FLAG_DEBUGGABLE;
}
return applicationInfo;
}
};
}
});
try (var withSystemPropertyOverrides =
new AndroidOsSystemProperties.WithOverridesForTesting(
traceNetLogSystemPropertyValue == null
? Map.of()
: Map.of(
CronetLibraryLoader
.TRACE_NET_LOG_SYSTEM_PROPERTY_KEY,
traceNetLogSystemPropertyValue));
var withBuildOverride =
new AndroidOsBuild.WithOverrideForTesting(
new AndroidOsBuild(/* type= */ buildType))) {
// Make sure Cronet is ready, so that we don't race against the init thread which is
// where trace netlog is initialized.
runOneRequest();
return CronetLibraryLoader.getTraceNetLogCaptureModeForTesting();
}
}
@Test
@SmallTest
@IgnoreFor(
implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM},
reason =
"FALLBACK: does not support netlog. "
+ "AOSP_PLATFORM: emulator image does not have this code yet.")
public void testDefaultTraceNetLogCaptureMode() throws Exception {
assertThat(
getTraceNetLogCaptureMode(
/* traceNetLogSystemPropertyValue= */ null,
/* buildType= */ "user",
/* appIsDebuggable= */ false))
.isEqualTo(NetLogCaptureMode.HEAVILY_REDACTED);
}
@Test
@SmallTest
@IgnoreFor(
implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM},
reason =
"FALLBACK: does not support netlog. "
+ "AOSP_PLATFORM: emulator image does not have this code yet.")
public void testSetTraceNetLogCaptureModeToHeavilyRedacted() throws Exception {
assertThat(
getTraceNetLogCaptureMode(
/* traceNetLogSystemPropertyValue= */ "heavily_redacted",
/* buildType= */ "user",
/* appIsDebuggable= */ false))
.isEqualTo(NetLogCaptureMode.HEAVILY_REDACTED);
}
@Test
@SmallTest
@IgnoreFor(
implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM},
reason =
"FALLBACK: does not support netlog. "
+ "AOSP_PLATFORM: emulator image does not have this code yet.")
public void testSetTraceNetLogCaptureModeOnDebuggableApp() throws Exception {
assertThat(
getTraceNetLogCaptureMode(
/* traceNetLogSystemPropertyValue= */ "on",
/* buildType= */ "user",
/* appIsDebuggable= */ true))
.isEqualTo(NetLogCaptureMode.DEFAULT);
}
@Test
@SmallTest
@IgnoreFor(
implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM},
reason =
"FALLBACK: does not support netlog. "
+ "AOSP_PLATFORM: emulator image does not have this code yet.")
public void testSetTraceNetLogCaptureModeOnDebugBuild() throws Exception {
assertThat(
getTraceNetLogCaptureMode(
/* traceNetLogSystemPropertyValue= */ "on",
/* buildType= */ "userdebug",
/* appIsDebuggable= */ false))
.isEqualTo(NetLogCaptureMode.DEFAULT);
}
@Test
@SmallTest
@IgnoreFor(
implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM},
reason =
"FALLBACK: does not support netlog. "
+ "AOSP_PLATFORM: emulator image does not have this code yet.")
public void testCannotSetTraceNetLogCaptureModeOnNonDebug() throws Exception {
assertThat(
getTraceNetLogCaptureMode(
/* traceNetLogSystemPropertyValue= */ "on",
/* buildType= */ "user",
/* appIsDebuggable= */ false))
.isEqualTo(NetLogCaptureMode.HEAVILY_REDACTED);
}
@Test
@SmallTest
@IgnoreFor(
implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM},
reason =
"FALLBACK: does not support netlog. "
+ "AOSP_PLATFORM: emulator image does not have this code yet.")
public void testIgnoresUnknownTraceNetLogSystemPropertyValue() throws Exception {
assertThat(
getTraceNetLogCaptureMode(
/* traceNetLogSystemPropertyValue= */ "invalid",
/* buildType= */ "userdebug",
/* appIsDebuggable= */ true))
.isEqualTo(NetLogCaptureMode.HEAVILY_REDACTED);
}
@Test
@SmallTest
@SuppressWarnings("deprecation")

15
net/log/BUILD.gn Normal file

@ -0,0 +1,15 @@
# Copyright 2025 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build/config/android/rules.gni")
java_cpp_enum("net_log_capture_mode_srcjar") {
visibility = [ ":*" ]
sources = [ "net_log_capture_mode.h" ]
}
android_library("net_log_capture_mode_java") {
srcjar_deps = [ ":net_log_capture_mode_srcjar" ]
deps = [ "//third_party/androidx:androidx_annotation_annotation_java" ]
}

@ -21,7 +21,9 @@ namespace net {
//
// Note the numeric values are used in a bitfield (NetLogCaptureModeSet) so must
// be sequential starting from 0, and not exceed 31.
enum class NetLogCaptureMode : uint32_t {
//
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.net
enum class NetLogCaptureMode : uint8_t {
// Logging level that only allows fields that are known to be safe from a
// privacy perspective.
//
@ -66,11 +68,11 @@ enum class NetLogCaptureMode : uint32_t {
// value "i".
//
// Use the NetLogCaptureModeSet*() functions to operate on it.
using NetLogCaptureModeSet = uint32_t;
using NetLogCaptureModeSet = uint8_t;
inline NetLogCaptureModeSet NetLogCaptureModeToBit(
NetLogCaptureMode capture_mode) {
return 1 << static_cast<uint32_t>(capture_mode);
return static_cast<uint8_t>(1 << static_cast<uint8_t>(capture_mode));
}
inline bool NetLogCaptureModeSetContains(NetLogCaptureMode capture_mode,

@ -24,6 +24,8 @@ namespace {
// TraceLog category for NetLog events.
constexpr const char kNetLogTracingCategory[] = "netlog";
constexpr const char kSensitiveNetLogTracingCategory[] =
TRACE_DISABLED_BY_DEFAULT("netlog.sensitive");
class TracedValue : public base::trace_event::ConvertableToTraceFormat {
public:
@ -48,7 +50,8 @@ class TracedValue : public base::trace_event::ConvertableToTraceFormat {
} // namespace
TraceNetLogObserver::TraceNetLogObserver(Options options)
: capture_mode_(options.capture_mode) {}
: capture_mode_(options.capture_mode),
use_sensitive_category_(options.use_sensitive_category) {}
TraceNetLogObserver::~TraceNetLogObserver() {
DCHECK(!net_log_to_watch_);
@ -61,26 +64,40 @@ void TraceNetLogObserver::OnAddEntry(const NetLogEntry& entry) {
params.Set("source_start_time",
NetLog::TickCountToString(entry.source.start_time));
const auto track = perfetto::Track(track_id_base_ + entry.source.id);
// The tracing category must be a compile-time constant, hence the macro to
// handle the sensitive vs non-sensitive categories.
#define CALL_TRACE_EVENT(TRACE_EVENT, ...) \
do { \
if (use_sensitive_category_) { \
TRACE_EVENT(kSensitiveNetLogTracingCategory, __VA_ARGS__); \
} else { \
TRACE_EVENT(kNetLogTracingCategory, __VA_ARGS__); \
} \
} while (false)
switch (entry.phase) {
case NetLogEventPhase::BEGIN:
TRACE_EVENT_BEGIN(
kNetLogTracingCategory,
CALL_TRACE_EVENT(
TRACE_EVENT_BEGIN,
perfetto::StaticString(NetLogEventTypeToString(entry.type)), track,
"source_type", NetLog::SourceTypeToString(entry.source.type),
"params", std::make_unique<TracedValue>(std::move(params)));
break;
case NetLogEventPhase::END:
TRACE_EVENT_END(kNetLogTracingCategory, track, "params",
std::make_unique<TracedValue>(std::move(params)));
CALL_TRACE_EVENT(TRACE_EVENT_END, track, "params",
std::make_unique<TracedValue>(std::move(params)));
break;
case NetLogEventPhase::NONE:
TRACE_EVENT_INSTANT(
kNetLogTracingCategory,
CALL_TRACE_EVENT(
TRACE_EVENT_INSTANT,
perfetto::StaticString(NetLogEventTypeToString(entry.type)), track,
"source_type", NetLog::SourceTypeToString(entry.source.type),
"params", std::make_unique<TracedValue>(std::move(params)));
break;
}
#undef CALL_TRACE_EVENT
}
void TraceNetLogObserver::WatchForTraceStart(NetLog* netlog) {

@ -26,6 +26,16 @@ class NET_EXPORT TraceNetLogObserver
static Options Default() { return {}; }
NetLogCaptureMode capture_mode = NetLogCaptureMode::kDefault;
// If false, trace events will be logged under the "netlog" category.
// If true, trace events will be logged under the
// "disabled-by-default-netlog.sensitive" category.
//
// TODO(https://crbug.com/410018349): ideally this should be derived from
// `capture_mode`, i.e. we should treat this as true if `capture_mode` is
// not `kHeavilyRedacted`. We'd need to assess the consequences on current
// trace users, though.
bool use_sensitive_category = false;
};
explicit TraceNetLogObserver(Options options = Options::Default());
@ -57,6 +67,7 @@ class NET_EXPORT TraceNetLogObserver
const uint64_t track_id_base_ = base::RandUint64();
const NetLogCaptureMode capture_mode_;
const bool use_sensitive_category_;
raw_ptr<NetLog> net_log_to_watch_ = nullptr;
base::WeakPtrFactory<TraceNetLogObserver> weak_factory_{this};
};

@ -72,7 +72,7 @@ enum NetLogEventPhase {
// changes.
interface NetLogProxySource {
// |modes| is a NetLogCaptureModeSet, a bitfield.
UpdateCaptureModes(uint32 modes);
UpdateCaptureModes(uint8 modes);
};
// Receives NetLog events from another process and inserts them into the main