Version 3.7.0-0.0.dev

Merge 6d2200b7a65f034719335e23809e375b25ee9e6f into dev
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c762e33..65437ce 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,5 @@
+## 3.7.0
+
 ## 3.6.0
 
 ### Language
diff --git a/DEPS b/DEPS
index edbdcbc..8c4bdd5 100644
--- a/DEPS
+++ b/DEPS
@@ -120,8 +120,8 @@
   # EOL comment after a dependency to disable this and pin it at its current
   # revision.
 
-  "args_rev": "e623652744c82533829f2e62b1aba1a6cf06e291",
-  "async_rev": "c0d81f8699682d01d657a9bf827107d11904a247",
+  "args_rev": "e623652744c82533829f2e62b1aba1a6cf06e291", #
+  "async_rev": "5f70a996f673d625e3502597084653686c3e754c",
   "bazel_worker_rev": "aa3cc9e826350b960e0c5a67e6065bcedba8b0ac",
   "benchmark_harness_rev": "44f125ae1d045aa3de09fe88a8dd70cb7352d563",
   "boolean_selector_rev": "d6c7c36ae1111f11cc24306d71d3ab2deea8fa68",
@@ -129,9 +129,9 @@
   "characters_rev": "7633a16a22c626e19ca750223237396315268a06",
   "cli_util_rev": "c36b3941e38092d6d6f87ac27d9e88f153d3ac38",
   "clock_rev": "7956d60042f4ea979c4554d43eeb57d087627869",
-  "collection_rev": "24b75d85df6a26aac7be13b56ff1ce4360c04a64",
-  "convert_rev": "9035cafefc1da4315f26058734d0c2a19d5ab56a",
-  "crypto_rev": "eede7d6918c51159c1422b7449f40dbac660ee57",
+  "collection_rev": "887b826b50f48d6a9cd2c0684aa353e8e3a0fad0",
+  "convert_rev": "d361833e117cb2438d2a2a6d0b0acb28ff0910fb",
+  "crypto_rev": "3d26ef4cf22d4b218ba30e616544ad3cf52f64a1",
   "csslib_rev": "a3700b05bbcc42782e8a7024790dbf019d89c249",
   # Note: Updates to dart_style have to be coordinated with the infrastructure
   # team so that the internal formatter `tools/sdks/dart-sdk/bin/dart format`
@@ -153,9 +153,9 @@
   "html_rev": "6d3bc86cf2ab530ef3fa5f84b5980dc318a02af4",
   "http_rev": "f59cd79e1322c6272481e4f2ccfa9afcb37a6525",
   "http_multi_server_rev": "e7515b5896b83d522189802a1e14e103e19426c0",
-  "http_parser_rev": "ce528cf82f3d26ac761e29b2494a9e0c270d4939",
+  "http_parser_rev": "23d775898ee90be9daf3297e298a8869bc755d84",
   "intl_rev": "5d65e3808ce40e6282e40881492607df4e35669f",
-  "json_rpc_2_rev": "b4810dc7bee5828f240586c81f3f34853cacdbce",
+  "json_rpc_2_rev": "c9b616bded8cdb5bfdc836ba7648afa6aba40062",
   "leak_tracker_rev": "f5620600a5ce1c44f65ddaa02001e200b096e14c", # manually rolled
   "lints_rev": "5016d63c889936b2100520f999591d0492d9afe3",
   "logging_rev": "6fa056098ceca03d399bff64592822b2ae5dee6e",
@@ -166,7 +166,7 @@
   "native_rev": "659511886501bcce638c3966590df04984909ef0", # dart-native-interop-team@ is rolling breaking changes manually while the assets features are in experimental.
   "package_config_rev": "bafff8e90be25e1985f7e3ee40ea1d22571a93e6",
   "path_rev": "e969f42ed112dd702a9453beb9df6c12ae2d3805",
-  "pool_rev": "924fb04353cec915d927f9f1aed88e2eda92b98a",
+  "pool_rev": "7bfc71b39742753a88688e56e55a828a2f5dc0bf",
   "protobuf_rev": "ccf104dbc36929c0f8708285d5f3a8fae206343e",
   "pub_rev": "1efd3f5e274e153637d99698b0ee454f6f2550ab", # disable tools/rev_sdk_deps.dart
   "pub_semver_rev": "8cce9d00431b6653026cdfcf6cf8548078c56f02",
@@ -184,17 +184,17 @@
   "test_descriptor_rev": "a3db1efe3dc725dcae9ee61647d3bfc19b3231ac",
   "test_process_rev": "52ee3f5ab70ed965bb7122c1d499081fbccd0bde",
   "test_reflective_loader_rev": "598af2f503955020af0eaa82558d574a03934078",
-  "tools_rev": "d4995d47b99d5e9564abfed2218f4a23df75983b",
-  "typed_data_rev": "2bb9e6ead6394e2d4ec6068c5ece8b2ec0e2b945",
+  "tools_rev": "d4995d47b99d5e9564abfed2218f4a23df75983b", #
+  "typed_data_rev": "6abfafdcf661cd8a814619d7e2a3e99edb3a3862",
   "vector_math_rev": "2cfbe2c115a57b368ccbc3c89ebd38a06764d3d1",
   "watcher_rev": "3b850778ad0b62db3aa2cfe48832870c2461db30",
   "web_rev": "8478cd27d574249eca3d41f9135458dfda2762c8",
-  "web_socket_channel_rev": "0e1d6e2eb5a0bfd62e45b772ac7107d796176cf6",
+  "web_socket_channel_rev": "40aa29f1d2167467f5934d755891a8beb62a1239",
   "webdev_rev": "5f30c560dc4e3df341356c43ec1a766ee6b74a7c",
   "webdriver_rev": "accfed557c005c87df56ffb626458f87da9f2125",
   "webkit_inspection_protocol_rev": "b459c427b74bf5e0919a083a97a167fb74d8bff1",
   "yaml_rev": "e773005ab84e1b4d24132b0a687be7f9a3bfda15",
-  "yaml_edit_rev": "5c54d455f272bbb83c948ac420c677371e69ae77",
+  "yaml_edit_rev": "35f4248c7bbba289b3899fa55486e2f31ef1a8c5",
 
   # Windows deps
   "crashpad_rev": "bf327d8ceb6a669607b0dbab5a83a275d03f99ed",
diff --git a/pkg/_fe_analyzer_shared/lib/src/experiments/flags.dart b/pkg/_fe_analyzer_shared/lib/src/experiments/flags.dart
index 96b4039..644ec4a 100644
--- a/pkg/_fe_analyzer_shared/lib/src/experiments/flags.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/experiments/flags.dart
@@ -6,7 +6,7 @@
 //
 // Instead modify 'tools/experimental_features.yaml' and run
 // 'dart pkg/front_end/tool/fasta.dart generate-experimental-flags' to update.
-const Version defaultLanguageVersion = const Version(3, 6);
+const Version defaultLanguageVersion = const Version(3, 7);
 
 /// Enum for experimental flags shared between the CFE and the analyzer.
 enum ExperimentalFlag {
diff --git a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
index 3255c82..cb25638 100644
--- a/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/flow_analysis/flow_analysis.dart
@@ -140,14 +140,21 @@
 /// while visiting the code for type inference.
 abstract class FlowAnalysis<Node extends Object, Statement extends Node,
     Expression extends Node, Variable extends Object, Type extends Object> {
-  factory FlowAnalysis(FlowAnalysisOperations<Variable, Type> operations,
-      AssignedVariables<Node, Variable> assignedVariables,
-      {required bool respectImplicitlyTypedVarInitializers,
-      required bool fieldPromotionEnabled}) {
-    return new _FlowAnalysisImpl(operations, assignedVariables,
-        respectImplicitlyTypedVarInitializers:
-            respectImplicitlyTypedVarInitializers,
-        fieldPromotionEnabled: fieldPromotionEnabled);
+  factory FlowAnalysis(
+    FlowAnalysisOperations<Variable, Type> operations,
+    AssignedVariables<Node, Variable> assignedVariables, {
+    required bool respectImplicitlyTypedVarInitializers,
+    required bool fieldPromotionEnabled,
+    required bool inferenceUpdate4Enabled,
+  }) {
+    return new _FlowAnalysisImpl(
+      operations,
+      assignedVariables,
+      respectImplicitlyTypedVarInitializers:
+          respectImplicitlyTypedVarInitializers,
+      fieldPromotionEnabled: fieldPromotionEnabled,
+      inferenceUpdate4Enabled: inferenceUpdate4Enabled,
+    );
   }
 
   factory FlowAnalysis.legacy(FlowAnalysisOperations<Variable, Type> operations,
@@ -1162,13 +1169,17 @@
   factory FlowAnalysisDebug(FlowAnalysisOperations<Variable, Type> operations,
       AssignedVariables<Node, Variable> assignedVariables,
       {required bool respectImplicitlyTypedVarInitializers,
-      required bool fieldPromotionEnabled}) {
+      required bool fieldPromotionEnabled,
+      required bool inferenceUpdate4Enabled}) {
     print('FlowAnalysisDebug()');
     return new FlowAnalysisDebug._(new _FlowAnalysisImpl(
-        operations, assignedVariables,
-        respectImplicitlyTypedVarInitializers:
-            respectImplicitlyTypedVarInitializers,
-        fieldPromotionEnabled: fieldPromotionEnabled));
+      operations,
+      assignedVariables,
+      respectImplicitlyTypedVarInitializers:
+          respectImplicitlyTypedVarInitializers,
+      fieldPromotionEnabled: fieldPromotionEnabled,
+      inferenceUpdate4Enabled: inferenceUpdate4Enabled,
+    ));
   }
 
   factory FlowAnalysisDebug.legacy(
@@ -4299,6 +4310,8 @@
 
   final bool fieldPromotionEnabled;
 
+  final bool inferenceUpdate4Enabled;
+
   @override
   final PromotionKeyStore<Variable> promotionKeyStore;
 
@@ -4315,10 +4328,13 @@
   @override
   final List<_Reference<Type>> _cascadeTargetStack = [];
 
-  _FlowAnalysisImpl(this.operations, this._assignedVariables,
-      {required this.respectImplicitlyTypedVarInitializers,
-      required this.fieldPromotionEnabled})
-      : promotionKeyStore = _assignedVariables.promotionKeyStore {
+  _FlowAnalysisImpl(
+    this.operations,
+    this._assignedVariables, {
+    required this.respectImplicitlyTypedVarInitializers,
+    required this.fieldPromotionEnabled,
+    required this.inferenceUpdate4Enabled,
+  }) : promotionKeyStore = _assignedVariables.promotionKeyStore {
     if (!_assignedVariables.isFinished) {
       _assignedVariables.finish();
     }
diff --git a/pkg/_fe_analyzer_shared/pubspec.yaml b/pkg/_fe_analyzer_shared/pubspec.yaml
index bedf240..18c71ae 100644
--- a/pkg/_fe_analyzer_shared/pubspec.yaml
+++ b/pkg/_fe_analyzer_shared/pubspec.yaml
@@ -1,5 +1,5 @@
 name: _fe_analyzer_shared
-version: 74.0.0
+version: 75.0.0
 description: Logic that is shared between the front_end and analyzer packages.
 repository: https://github.com/dart-lang/sdk/tree/main/pkg/_fe_analyzer_shared
 
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart
index c125b81..4dc13f8 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/flow_analysis_test.dart
@@ -608,7 +608,8 @@
           FlowAnalysis<Node, Statement, Expression, Var, SharedTypeView<Type>>(
               h.typeOperations, AssignedVariables<Node, Var>(),
               respectImplicitlyTypedVarInitializers: true,
-              fieldPromotionEnabled: true);
+              fieldPromotionEnabled: true,
+              inferenceUpdate4Enabled: true);
       flow.ifStatement_conditionBegin();
       flow.ifStatement_thenBegin(e, s);
       expect(() => flow.finish(), _asserts);
diff --git a/pkg/_fe_analyzer_shared/test/mini_ast.dart b/pkg/_fe_analyzer_shared/test/mini_ast.dart
index c8b4067..657105d 100644
--- a/pkg/_fe_analyzer_shared/test/mini_ast.dart
+++ b/pkg/_fe_analyzer_shared/test/mini_ast.dart
@@ -1674,6 +1674,8 @@
 
   bool? _inferenceUpdate3Enabled;
 
+  bool? _inferenceUpdate4Enabled;
+
   bool? _patternsEnabled;
 
   Type? _thisType;
@@ -1703,6 +1705,9 @@
   bool get inferenceUpdate3Enabled =>
       _inferenceUpdate3Enabled ?? !operations.legacy;
 
+  bool get inferenceUpdate4Enabled =>
+      _inferenceUpdate4Enabled ?? !operations.legacy;
+
   MiniIRBuilder get irBuilder => typeAnalyzer._irBuilder;
 
   bool get patternsEnabled => _patternsEnabled ?? !operations.legacy;
@@ -1787,6 +1792,11 @@
     _inferenceUpdate3Enabled = false;
   }
 
+  void disableInferenceUpdate4() {
+    assert(!_started);
+    _inferenceUpdate4Enabled = false;
+  }
+
   void disablePatterns() {
     assert(!_started);
     _patternsEnabled = false;
@@ -1879,10 +1889,14 @@
                   SharedTypeView<Type>>.legacy(
               operations, visitor._assignedVariables)
           : FlowAnalysis<Node, Statement, Expression, Var,
-                  SharedTypeView<Type>>(operations, visitor._assignedVariables,
+              SharedTypeView<Type>>(
+              operations,
+              visitor._assignedVariables,
               respectImplicitlyTypedVarInitializers:
                   _respectImplicitlyTypedVarInitializers,
-              fieldPromotionEnabled: _fieldPromotionEnabled);
+              fieldPromotionEnabled: _fieldPromotionEnabled,
+              inferenceUpdate4Enabled: inferenceUpdate4Enabled,
+            );
       typeAnalyzer.dispatchStatement(b);
       typeAnalyzer.finish();
       expect(typeAnalyzer.errors._accumulatedErrors, expectedErrors);
diff --git a/pkg/analysis_server/doc/api.html b/pkg/analysis_server/doc/api.html
index e91aa35..563e109 100644
--- a/pkg/analysis_server/doc/api.html
+++ b/pkg/analysis_server/doc/api.html
@@ -2321,7 +2321,9 @@
       </dd><dt class="field"><b>lineLength: int<span style="color:#999999"> (optional)</span></b></dt><dd>
         
         <p>
-          The line length to be used by the formatter.
+          The line length to be used by the formatter. This value is ignored if a
+          <tt>formatter.page_width</tt> has been configured in the relevant
+          <tt>analysis_options.yaml</tt> file.
         </p>
       </dd></dl><h4>returns:</h4><dl><dt class="field"><b>edits: List&lt;<a href="#type_SourceEdit">SourceEdit</a>&gt;</b></dt><dd>
         
diff --git a/pkg/analysis_server/lib/protocol/protocol_generated.dart b/pkg/analysis_server/lib/protocol/protocol_generated.dart
index 1c1456c..0d49ab42 100644
--- a/pkg/analysis_server/lib/protocol/protocol_generated.dart
+++ b/pkg/analysis_server/lib/protocol/protocol_generated.dart
@@ -5830,7 +5830,9 @@
   /// The length of the current selection in the file.
   int selectionLength;
 
-  /// The line length to be used by the formatter.
+  /// The line length to be used by the formatter. This value is ignored if a
+  /// formatter.page_width has been configured in the relevant
+  /// analysis_options.yaml file.
   int? lineLength;
 
   EditFormatParams(this.file, this.selectionOffset, this.selectionLength,
diff --git a/pkg/analysis_server/lib/src/handler/legacy/edit_format.dart b/pkg/analysis_server/lib/src/handler/legacy/edit_format.dart
index 4a5ceaf..e13eb98 100644
--- a/pkg/analysis_server/lib/src/handler/legacy/edit_format.dart
+++ b/pkg/analysis_server/lib/src/handler/legacy/edit_format.dart
@@ -7,6 +7,7 @@
 import 'package:analysis_server/protocol/protocol.dart';
 import 'package:analysis_server/protocol/protocol_generated.dart';
 import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:dart_style/src/dart_formatter.dart';
 import 'package:dart_style/src/exceptions.dart';
@@ -50,10 +51,17 @@
     );
 
     var driver = server.getAnalysisDriver(file);
-    var library = await driver?.getResolvedLibrary(file);
+    var unit = await driver?.getResolvedUnit(file);
+    var (pageWidth, effectiveLanguageVersion) = switch (unit) {
+      ResolvedUnitResult() => (
+          unit.analysisOptions.formatterOptions.pageWidth,
+          unit.libraryElement2.effectiveLanguageVersion,
+        ),
+      (_) => (null, null),
+    };
     var formatter = DartFormatter(
-        pageWidth: params.lineLength,
-        languageVersion: library.effectiveLanguageVersion);
+        pageWidth: pageWidth ?? params.lineLength,
+        languageVersion: effectiveLanguageVersion);
     SourceCode formattedResult;
     try {
       formattedResult = formatter.formatSource(code);
diff --git a/pkg/analysis_server/lib/src/handler/legacy/legacy_handler.dart b/pkg/analysis_server/lib/src/handler/legacy/legacy_handler.dart
index 6d006e9..7ebfa5a 100644
--- a/pkg/analysis_server/lib/src/handler/legacy/legacy_handler.dart
+++ b/pkg/analysis_server/lib/src/handler/legacy/legacy_handler.dart
@@ -10,6 +10,7 @@
 import 'package:analysis_server/src/legacy_analysis_server.dart';
 import 'package:analysis_server/src/protocol/protocol_internal.dart';
 import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/dart/element/element2.dart';
 import 'package:analyzer/error/error.dart';
 import 'package:analyzer/src/dart/error/syntactic_errors.g.dart';
 import 'package:analyzer/src/util/performance/operation_performance.dart';
@@ -100,6 +101,14 @@
   }
 }
 
+extension LibraryElement2Extension on LibraryElement2 {
+  // TODO(dantup): Remove this once we can use `languageVersion.effective`
+  //  in element-model-migrated files without triggering the lint.
+  Version get effectiveLanguageVersion {
+    return languageVersion.effective;
+  }
+}
+
 extension SomeResolvedLibraryResultExtension on SomeResolvedLibraryResult? {
   Version get effectiveLanguageVersion {
     var self = this;
diff --git a/pkg/analysis_server/lib/src/lsp/client_configuration.dart b/pkg/analysis_server/lib/src/lsp/client_configuration.dart
index 1482139..17545ea 100644
--- a/pkg/analysis_server/lib/src/lsp/client_configuration.dart
+++ b/pkg/analysis_server/lib/src/lsp/client_configuration.dart
@@ -286,7 +286,8 @@
         true;
   }
 
-  /// The line length used when formatting documents.
+  /// The line length used when formatting documents if not specified in
+  /// `analysis_options.yaml`.
   ///
   /// If null, the formatters default will be used.
   int? get lineLength =>
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_format_on_type.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_format_on_type.dart
index 53bb3f3..7def579 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_format_on_type.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_format_on_type.dart
@@ -39,7 +39,7 @@
     }
 
     var lineLength = server.lspClientConfiguration.forResource(path).lineLength;
-    return generateEditsForFormatting(result, lineLength);
+    return generateEditsForFormatting(result, defaultPageWidth: lineLength);
   }
 
   @override
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_format_range.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_format_range.dart
index 48297a9..eb39038 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_format_range.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_format_range.dart
@@ -39,7 +39,8 @@
     }
 
     var lineLength = server.lspClientConfiguration.forResource(path).lineLength;
-    return generateEditsForFormatting(result, lineLength, range: range);
+    return generateEditsForFormatting(result,
+        defaultPageWidth: lineLength, range: range);
   }
 
   @override
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_formatting.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_formatting.dart
index 22e6492..411bb66 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_formatting.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_formatting.dart
@@ -39,7 +39,7 @@
     }
 
     var lineLength = server.lspClientConfiguration.forResource(path).lineLength;
-    return generateEditsForFormatting(result, lineLength);
+    return generateEditsForFormatting(result, defaultPageWidth: lineLength);
   }
 
   @override
diff --git a/pkg/analysis_server/lib/src/lsp/source_edits.dart b/pkg/analysis_server/lib/src/lsp/source_edits.dart
index 7055cce..91fcdd0 100644
--- a/pkg/analysis_server/lib/src/lsp/source_edits.dart
+++ b/pkg/analysis_server/lib/src/lsp/source_edits.dart
@@ -78,13 +78,25 @@
   return ErrorOr.success((content: newContent, edits: serverEdits));
 }
 
+/// Generates a list of [TextEdit]s to format the code for [result].
+///
+/// [defaultPageWidth] will be used as the default page width if [result] does
+/// not have an analysis_options file that defines a page width.
+///
+/// If [range] is provided, only edits that intersect with this range will be
+/// returned.
 ErrorOr<List<TextEdit>?> generateEditsForFormatting(
-  ParsedUnitResult result,
-  int? lineLength, {
+  ParsedUnitResult result, {
+  int? defaultPageWidth,
   Range? range,
 }) {
   var unformattedSource = result.content;
 
+  // The analysis options page width always takes priority over the default from
+  // the LSP configuration.
+  var effectivePageWidth =
+      result.analysisOptions.formatterOptions.pageWidth ?? defaultPageWidth;
+
   var code = SourceCode(unformattedSource);
   SourceCode formattedResult;
   try {
@@ -94,9 +106,9 @@
     var languageVersion =
         result.unit.declaredElement?.library.languageVersion.effective ??
             DartFormatter.latestLanguageVersion;
-    formattedResult =
-        DartFormatter(pageWidth: lineLength, languageVersion: languageVersion)
-            .formatSource(code);
+    var formatter = DartFormatter(
+        pageWidth: effectivePageWidth, languageVersion: languageVersion);
+    formattedResult = formatter.formatSource(code);
   } on FormatterException {
     // If the document fails to parse, just return no edits to avoid the
     // use seeing edits on every save with invalid code (if LSP gains the
diff --git a/pkg/analysis_server/lib/src/services/completion/yaml/analysis_options_generator.dart b/pkg/analysis_server/lib/src/services/completion/yaml/analysis_options_generator.dart
index 62d845a..679f98f 100644
--- a/pkg/analysis_server/lib/src/services/completion/yaml/analysis_options_generator.dart
+++ b/pkg/analysis_server/lib/src/services/completion/yaml/analysis_options_generator.dart
@@ -39,6 +39,9 @@
     AnalyzerOptions.codeStyle: MapProducer({
       AnalyzerOptions.format: BooleanProducer(),
     }),
+    AnalyzerOptions.formatter: MapProducer({
+      AnalyzerOptions.pageWidth: EmptyProducer(),
+    }),
     // TODO(brianwilkerson): Create a producer to produce `package:` URIs.
     AnalyzerOptions.include: EmptyProducer(),
     // TODO(brianwilkerson): Create constants for 'linter' and 'rules'.
diff --git a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
index 2bd3783..2785d97 100644
--- a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
+++ b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
@@ -829,7 +829,7 @@
           continue;
         }
 
-        var formatResult = generateEditsForFormatting(result, null);
+        var formatResult = generateEditsForFormatting(result);
         await formatResult.mapResult((formatResult) async {
           var edits = formatResult ?? [];
           if (edits.isNotEmpty) {
diff --git a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
index 1fcdf41..27f02b5 100644
--- a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
+++ b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
@@ -151,9 +151,7 @@
   status: needsFix
   notes: |-
     For each exported name, add a fix to hide the name.
-CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_THREE_OR_MORE:
-  status: hasFix
-CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO:
+CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS:
   status: hasFix
 CompileTimeErrorCode.AMBIGUOUS_IMPORT:
   status: needsFix
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index b5fb4a4..30d44f6 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -747,10 +747,7 @@
 };
 
 final _builtInNonLintMultiProducers = {
-  CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO: [
-    AddExtensionOverride.new,
-  ],
-  CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_THREE_OR_MORE: [
+  CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS: [
     AddExtensionOverride.new,
   ],
   CompileTimeErrorCode.ARGUMENT_TYPE_NOT_ASSIGNABLE: [
diff --git a/pkg/analysis_server/test/domain_completion_test.dart b/pkg/analysis_server/test/domain_completion_test.dart
index 02d0c24..0f1075b 100644
--- a/pkg/analysis_server/test/domain_completion_test.dart
+++ b/pkg/analysis_server/test/domain_completion_test.dart
@@ -2035,6 +2035,8 @@
     kind: identifier
   |code-style: |
     kind: identifier
+  |formatter: |
+    kind: identifier
   |include: |
     kind: identifier
   |linter: |
diff --git a/pkg/analysis_server/test/edit/format_test.dart b/pkg/analysis_server/test/edit/format_test.dart
index b6e66c1..1356ac6 100644
--- a/pkg/analysis_server/test/edit/format_test.dart
+++ b/pkg/analysis_server/test/edit/format_test.dart
@@ -23,7 +23,49 @@
     await setRoots(included: [workspaceRootPath], excluded: []);
   }
 
-  Future<void> test_format_longLine() async {
+  Future<void> test_format_longLine_analysisOptions() async {
+    writeTestPackageAnalysisOptionsFile(r'''
+formatter:
+  page_width: 100
+''');
+    var content = '''
+fun(firstParam, secondParam, thirdParam, fourthParam) {
+  if (firstParam.noNull && secondParam.noNull && thirdParam.noNull && fourthParam.noNull) {}
+}
+''';
+    addTestFile(content);
+    await waitForTasksFinished();
+    var formatResult = await _formatAt(0, 3);
+
+    expect(formatResult.edits, isNotNull);
+    expect(formatResult.edits, hasLength(0));
+
+    expect(formatResult.selectionOffset, equals(0));
+    expect(formatResult.selectionLength, equals(3));
+  }
+
+  Future<void> test_format_longLine_analysisOptions_overridesParameter() async {
+    writeTestPackageAnalysisOptionsFile(r'''
+formatter:
+  page_width: 100
+''');
+    var content = '''
+fun(firstParam, secondParam, thirdParam, fourthParam) {
+  if (firstParam.noNull && secondParam.noNull && thirdParam.noNull && fourthParam.noNull) {}
+}
+''';
+    addTestFile(content);
+    await waitForTasksFinished();
+    var formatResult = await _formatAt(0, 3, lineLength: 50);
+
+    expect(formatResult.edits, isNotNull);
+    expect(formatResult.edits, hasLength(0));
+
+    expect(formatResult.selectionOffset, equals(0));
+    expect(formatResult.selectionLength, equals(3));
+  }
+
+  Future<void> test_format_longLine_parameter() async {
     var content = '''
 fun(firstParam, secondParam, thirdParam, fourthParam) {
   if (firstParam.noNull && secondParam.noNull && thirdParam.noNull && fourthParam.noNull) {}
diff --git a/pkg/analysis_server/test/integration/support/integration_test_methods.dart b/pkg/analysis_server/test/integration/support/integration_test_methods.dart
index f375d81..450344d 100644
--- a/pkg/analysis_server/test/integration/support/integration_test_methods.dart
+++ b/pkg/analysis_server/test/integration/support/integration_test_methods.dart
@@ -1566,7 +1566,9 @@
   ///
   /// lineLength: int (optional)
   ///
-  ///   The line length to be used by the formatter.
+  ///   The line length to be used by the formatter. This value is ignored if a
+  ///   formatter.page_width has been configured in the relevant
+  ///   analysis_options.yaml file.
   ///
   /// Returns
   ///
diff --git a/pkg/analysis_server/test/lsp/format_test.dart b/pkg/analysis_server/test/lsp/format_test.dart
index 823e5e0..d51df2e 100644
--- a/pkg/analysis_server/test/lsp/format_test.dart
+++ b/pkg/analysis_server/test/lsp/format_test.dart
@@ -20,6 +20,11 @@
 
 @reflectiveTest
 class FormatTest extends AbstractLspAnalysisServerTest {
+  /// Some sample code that is over 50 characters and will be wrapped if the
+  /// page width is set to 40.
+  static const codeThatWrapsAt40 =
+      "var a = '        10        20        30        40';";
+
   Future<List<TextEdit>> expectFormattedContents(
       Uri uri, String original, String expected) async {
     var formatEdits = (await formatDocument(uri))!;
@@ -36,6 +41,11 @@
     return formatEdits;
   }
 
+  Future<String> formatContents(Uri uri, String original) async {
+    var formatEdits = (await formatDocument(uri))!;
+    return applyTextEdits(original, formatEdits);
+  }
+
   Future<void> test_alreadyFormatted() async {
     const contents = '''
 void f() {
@@ -383,6 +393,83 @@
     await expectFormattedContents(mainFileUri, contents, expectedLongLines);
   }
 
+  Future<void> test_lineLength_analysisOptions() async {
+    const codeContent = codeThatWrapsAt40;
+    const optionsContent = '''
+formatter:
+  page_width: 40
+''';
+
+    newFile(analysisOptionsPath, optionsContent);
+    newFile(mainFilePath, codeContent);
+    await initialize();
+
+    // Ignore trailing newlines when checking for wrapping.
+    var formatted = await formatContents(mainFileUri, codeContent);
+    expect(formatted.trim(), contains('\n'));
+  }
+
+  Future<void> test_lineLength_analysisOptions_nestedEmpty() async {
+    const codeContent = codeThatWrapsAt40;
+    const optionsContent = '''
+formatter:
+  page_width: 40
+''';
+    var nestedAnalysisOptionsPath =
+        join(projectFolderPath, 'lib', 'analysis_options.yaml');
+
+    newFile(analysisOptionsPath, optionsContent);
+    newFile(
+        nestedAnalysisOptionsPath, '# empty'); // suppress the parent options.
+    newFile(mainFilePath, codeContent);
+    await initialize();
+
+    // Ignore trailing newlines when checking for wrapping.
+    var formatted = await formatContents(mainFileUri, codeContent);
+    expect(formatted.trim(), isNot(contains('\n')));
+  }
+
+  Future<void> test_lineLength_analysisOptions_nestedIncludes() async {
+    const codeContent = codeThatWrapsAt40;
+    const optionsContent = '''
+formatter:
+  page_width: 40
+''';
+    var nestedAnalysisOptionsPath =
+        join(projectFolderPath, 'lib', 'analysis_options.yaml');
+
+    newFile(analysisOptionsPath, optionsContent);
+    newFile(nestedAnalysisOptionsPath, 'include: ../analysis_options.yaml');
+    newFile(mainFilePath, codeContent);
+    await initialize();
+
+    // Ignore trailing newlines when checking for wrapping.
+    var formatted = await formatContents(mainFileUri, codeContent);
+    expect(formatted.trim(), contains('\n'));
+  }
+
+  Future<void> test_lineLength_analysisOptions_overridesConfig() async {
+    const codeContent = codeThatWrapsAt40;
+    const optionsContent = '''
+formatter:
+  page_width: 40
+''';
+
+    newFile(analysisOptionsPath, optionsContent);
+    newFile(mainFilePath, codeContent);
+    await provideConfig(
+      initialize,
+      {
+        // This won't apply because analysis_options wins.
+        'lineLength': 100,
+      },
+    );
+
+    // Ignore trailing newlines when checking for wrapping.
+    var formatted = await formatContents(mainFileUri, codeContent);
+    expect(formatted.trim(), contains('\n'));
+  }
+
   Future<void> test_lineLength_outsideWorkspaceFolders() async {
     const contents = '''
 void f() {
diff --git a/pkg/analysis_server/test/src/services/completion/yaml/analysis_options_generator_test.dart b/pkg/analysis_server/test/src/services/completion/yaml/analysis_options_generator_test.dart
index 8a05a37..51c80e8 100644
--- a/pkg/analysis_server/test/src/services/completion/yaml/analysis_options_generator_test.dart
+++ b/pkg/analysis_server/test/src/services/completion/yaml/analysis_options_generator_test.dart
@@ -128,11 +128,20 @@
     getCompletions('^');
     assertSuggestion('${AnalyzerOptions.analyzer}: ');
     assertSuggestion('${AnalyzerOptions.codeStyle}: ');
+    assertSuggestion('${AnalyzerOptions.formatter}: ');
     assertSuggestion('${AnalyzerOptions.include}: ');
     // TODO(brianwilkerson): Replace this with a constant.
     assertSuggestion('linter: ');
   }
 
+  void test_formatter() {
+    getCompletions('''
+formatter:
+  ^
+''');
+    assertSuggestion('${AnalyzerOptions.pageWidth}: ');
+  }
+
   void test_linter() {
     getCompletions('''
 linter:
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_extension_override_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_extension_override_test.dart
index dbb2b44..6e3f949 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/add_extension_override_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_extension_override_test.dart
@@ -44,7 +44,7 @@
 }
 ''', expectedNumberOfFixesForKind: 1, errorFilter: (error) {
       return error.errorCode ==
-          CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO;
+          CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS;
     });
   }
 
diff --git a/pkg/analysis_server/tool/lsp_spec/README.md b/pkg/analysis_server/tool/lsp_spec/README.md
index 05abbef..181649c 100644
--- a/pkg/analysis_server/tool/lsp_spec/README.md
+++ b/pkg/analysis_server/tool/lsp_spec/README.md
@@ -36,7 +36,7 @@
 
 - `dart.analysisExcludedFolders` (`List<String>?`): An array of paths (absolute or relative to each workspace folder) that should be excluded from analysis.
 - `dart.enableSdkFormatter` (`bool?`): When set to `false`, prevents registration (or unregisters) the SDK formatter. When set to `true` or not supplied, will register/reregister the SDK formatter.
-- `dart.lineLength` (`int?`): The number of characters the formatter should wrap code at. If unspecified, code will be wrapped at `80` characters.
+- `dart.lineLength` (`int?`): Sets a default value for the formatter to wrap code at if no value is specified in `formatter.page_width` in `analysis_options.yaml`. If unspecified by both, code will be wrapped at `80` characters.
 - `dart.completeFunctionCalls` (`bool?`): When set to true, completes functions/methods with their required parameters.
 - `dart.showTodos` (`bool?`): Whether to generate diagnostics for TODO comments. If unspecified, diagnostics will not be generated.
 - `dart.renameFilesWithClasses` (`String`): When set to `"always"`, will include edits to rename files when classes are renamed if the filename matches the class name (but in snake_form). When set to `"prompt"`, a prompt will be shown on each class rename asking to confirm the file rename. Otherwise, files will not be renamed. Renames are performed using LSP's ResourceOperation edits - that means the rename is simply included in the resulting `WorkspaceEdit` and must be handled by the client.
diff --git a/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java b/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java
index 8193f72..37b47f5 100644
--- a/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java
+++ b/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java
@@ -511,7 +511,8 @@
    * @param file The file containing the code to be formatted.
    * @param selectionOffset The offset of the current selection in the file.
    * @param selectionLength The length of the current selection in the file.
-   * @param lineLength The line length to be used by the formatter.
+   * @param lineLength The line length to be used by the formatter. This value is ignored if a
+   *        formatter.page_width has been configured in the relevant analysis_options.yaml file.
    */
   public void edit_format(String file, int selectionOffset, int selectionLength, int lineLength, FormatConsumer consumer);
 
diff --git a/pkg/analysis_server/tool/spec/spec_input.html b/pkg/analysis_server/tool/spec/spec_input.html
index aed13fb..6d11b0c 100644
--- a/pkg/analysis_server/tool/spec/spec_input.html
+++ b/pkg/analysis_server/tool/spec/spec_input.html
@@ -2186,7 +2186,9 @@
       <field name="lineLength" optional="true">
         <ref>int</ref>
         <p>
-          The line length to be used by the formatter.
+          The line length to be used by the formatter. This value is ignored if a
+          <tt>formatter.page_width</tt> has been configured in the relevant
+          <tt>analysis_options.yaml</tt> file.
         </p>
       </field>
     </params>
diff --git a/pkg/analysis_server_client/lib/src/protocol/protocol_generated.dart b/pkg/analysis_server_client/lib/src/protocol/protocol_generated.dart
index 1970aac..64ebc2f 100644
--- a/pkg/analysis_server_client/lib/src/protocol/protocol_generated.dart
+++ b/pkg/analysis_server_client/lib/src/protocol/protocol_generated.dart
@@ -5253,7 +5253,9 @@
   /// The length of the current selection in the file.
   int selectionLength;
 
-  /// The line length to be used by the formatter.
+  /// The line length to be used by the formatter. This value is ignored if a
+  /// formatter.page_width has been configured in the relevant
+  /// analysis_options.yaml file.
   int? lineLength;
 
   EditFormatParams(this.file, this.selectionOffset, this.selectionLength,
diff --git a/pkg/analyzer/CHANGELOG.md b/pkg/analyzer/CHANGELOG.md
index 80913ab..e68cbe2 100644
--- a/pkg/analyzer/CHANGELOG.md
+++ b/pkg/analyzer/CHANGELOG.md
@@ -1,4 +1,4 @@
-## 6.10.0-dev
+## 6.10.0
 * Deprecated `LibraryElement.accessibleExtensions`, use
   `CompilationUnitElement.accessibleExtensions` instead.
 * Deprecated `LibraryElement.exportedLibraries`.
diff --git a/pkg/analyzer/lib/dart/analysis/analysis_options.dart b/pkg/analyzer/lib/dart/analysis/analysis_options.dart
index 84c11ff..93ea731 100644
--- a/pkg/analyzer/lib/dart/analysis/analysis_options.dart
+++ b/pkg/analyzer/lib/dart/analysis/analysis_options.dart
@@ -4,6 +4,7 @@
 
 import 'package:analyzer/dart/analysis/code_style_options.dart';
 import 'package:analyzer/dart/analysis/features.dart';
+import 'package:analyzer/dart/analysis/formatter_options.dart';
 import 'package:analyzer/source/error_processor.dart';
 import 'package:analyzer/src/lint/linter.dart';
 import 'package:pub_semver/src/version_constraint.dart';
@@ -38,6 +39,9 @@
   /// analysis.
   List<String> get excludePatterns;
 
+  /// Return the options used to control the formatter.
+  FormatterOptions get formatterOptions;
+
   /// Return `true` if analysis is to generate hint results (e.g. best practices
   /// and analysis based on certain annotations).
   @Deprecated("Use 'warning' instead")
diff --git a/pkg/analyzer/lib/dart/analysis/formatter_options.dart b/pkg/analyzer/lib/dart/analysis/formatter_options.dart
new file mode 100644
index 0000000..a6837fd9
--- /dev/null
+++ b/pkg/analyzer/lib/dart/analysis/formatter_options.dart
@@ -0,0 +1,12 @@
+// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// A set of options related to the formatter that apply to the code within a
+/// single analysis context.
+///
+/// Clients may not extend, implement or mix-in this class.
+abstract class FormatterOptions {
+  /// The width configured for where the formatter should wrap code.
+  int? get pageWidth;
+}
diff --git a/pkg/analyzer/lib/dart/element/element2.dart b/pkg/analyzer/lib/dart/element/element2.dart
index 909e6e0..42bafae 100644
--- a/pkg/analyzer/lib/dart/element/element2.dart
+++ b/pkg/analyzer/lib/dart/element/element2.dart
@@ -202,6 +202,9 @@
   ConstructorElement2 get baseElement;
 
   @override
+  InterfaceElement2 get enclosingElement2;
+
+  @override
   ConstructorFragment? get firstFragment;
 
   /// Whether the constructor is a const constructor.
diff --git a/pkg/analyzer/lib/error/listener.dart b/pkg/analyzer/lib/error/listener.dart
index 7a50e6e..0896322 100644
--- a/pkg/analyzer/lib/error/listener.dart
+++ b/pkg/analyzer/lib/error/listener.dart
@@ -11,10 +11,8 @@
 import 'package:analyzer/diagnostic/diagnostic.dart';
 import 'package:analyzer/error/error.dart';
 import 'package:analyzer/source/source.dart';
-import 'package:analyzer/src/dart/element/extensions.dart';
 import 'package:analyzer/src/dart/element/type.dart';
 import 'package:analyzer/src/diagnostic/diagnostic.dart';
-import 'package:analyzer/src/utilities/extensions/collection.dart';
 import 'package:meta/meta.dart';
 import 'package:source_span/source_span.dart';
 
@@ -170,19 +168,7 @@
       return;
     }
 
-    if (arguments != null) {
-      var invalid = arguments
-          .whereNotType<String>()
-          .whereNotType<DartType>()
-          .whereNotType<Element>()
-          .whereNotType<int>()
-          .whereNotType<Uri>();
-      if (invalid.isNotEmpty) {
-        throw ArgumentError('Tried to format an error using '
-            '${invalid.map((e) => e.runtimeType).join(', ')}');
-      }
-    }
-
+    _convertElements(arguments);
     contextMessages ??= [];
     contextMessages.addAll(_convertTypeNames(arguments));
     _errorListener.onError(
@@ -348,76 +334,92 @@
     );
   }
 
-  /// Given an array of [arguments] that may contain [DartType]s and [Element]s,
-  /// converts the types and elements into strings by using the display names of
-  /// each, unless there are two or more types or elements with the same display
-  /// names, in which case the extended display names will be used in order to
-  /// clarify the message.
-  List<DiagnosticMessage> _convertTypeNames(List<Object?>? arguments) {
+  /// Convert all [Element]s in the [arguments] into their display strings.
+  void _convertElements(List<Object>? arguments) {
     if (arguments == null) {
-      return const [];
+      return;
     }
 
-    var typeGroups = <String, List<_ToConvert>>{};
     for (var i = 0; i < arguments.length; i++) {
       var argument = arguments[i];
-      if (argument is TypeImpl) {
-        var displayName = argument.getDisplayString(preferTypeAlias: true);
-        var types = typeGroups.putIfAbsent(displayName, () => []);
-        types.add(_TypeToConvert(i, argument, displayName));
-      } else if (argument is Element) {
-        var displayName = argument.getDisplayString();
-        var types = typeGroups.putIfAbsent(displayName, () => []);
-        types.add(_ElementToConvert(i, argument, displayName));
+      if (argument is Element) {
+        arguments[i] = argument.getDisplayString();
+      } else if (!(argument is String ||
+          argument is DartType ||
+          argument is int ||
+          argument is Uri)) {
+        throw ArgumentError(
+            'Tried to format an error using ${argument.runtimeType}');
       }
     }
+  }
 
+  /// Given an array of [arguments] that is expected to contain two or more
+  /// types, convert the types into strings by using the display names of the
+  /// types, unless there are two or more types with the same names, in which
+  /// case the extended display names of the types will be used in order to
+  /// clarify the message.
+  List<DiagnosticMessage> _convertTypeNames(List<Object?>? arguments) {
     var messages = <DiagnosticMessage>[];
-    for (var typeGroup in typeGroups.values) {
+    if (arguments == null) {
+      return messages;
+    }
+
+    Map<String, List<_TypeToConvert>> typeGroups = {};
+    for (int i = 0; i < arguments.length; i++) {
+      var argument = arguments[i];
+      if (argument is TypeImpl) {
+        String displayName = argument.getDisplayString(
+          preferTypeAlias: true,
+        );
+        List<_TypeToConvert> types =
+            typeGroups.putIfAbsent(displayName, () => <_TypeToConvert>[]);
+        types.add(_TypeToConvert(i, argument, displayName));
+      }
+    }
+    for (List<_TypeToConvert> typeGroup in typeGroups.values) {
       if (typeGroup.length == 1) {
-        var typeToConvert = typeGroup[0];
-        // If the display name of a type is unambiguous, just replace the type
-        // in the arguments list with its display name.
+        _TypeToConvert typeToConvert = typeGroup[0];
         arguments[typeToConvert.index] = typeToConvert.displayName;
-        continue;
-      }
-
-      var nameToElementMap = <String, Set<Element>>{};
-      for (var typeToConvert in typeGroup) {
-        for (var element in typeToConvert.allElements) {
-          var elements = nameToElementMap.putIfAbsent(element.name!, () => {});
-          elements.add(element);
-        }
-      }
-
-      for (var typeToConvert in typeGroup) {
-        // TODO(brianwilkerson): When clients do a better job of displaying
-        // context messages, remove the extra text added to the buffer.
-        StringBuffer? buffer;
-        for (var element in typeToConvert.allElements) {
-          var name = element.name!;
-          var sourcePath = element.source!.fullName;
-          if (nameToElementMap[name]!.length > 1) {
-            if (buffer == null) {
-              buffer = StringBuffer();
-              buffer.write('where ');
-            } else {
-              buffer.write(', ');
-            }
-            buffer.write('$name is defined in $sourcePath');
+      } else {
+        Map<String, Set<Element>> nameToElementMap = {};
+        for (_TypeToConvert typeToConvert in typeGroup) {
+          for (Element element in typeToConvert.allElements()) {
+            Set<Element> elements =
+                nameToElementMap.putIfAbsent(element.name!, () => <Element>{});
+            elements.add(element);
           }
-          messages.add(DiagnosticMessageImpl(
-            filePath: element.source!.fullName,
-            length: element.nameLength,
-            message: '$name is defined in $sourcePath',
-            offset: element.nameOffset,
-            url: null,
-          ));
         }
+        for (_TypeToConvert typeToConvert in typeGroup) {
+          // TODO(brianwilkerson): When clients do a better job of displaying
+          // context messages, remove the extra text added to the buffer.
+          StringBuffer? buffer;
+          for (Element element in typeToConvert.allElements()) {
+            String name = element.name!;
+            if (nameToElementMap[name]!.length > 1) {
+              if (buffer == null) {
+                buffer = StringBuffer();
+                buffer.write('where ');
+              } else {
+                buffer.write(', ');
+              }
+              buffer.write('$name is defined in ${element.source!.fullName}');
+            }
+            messages.add(DiagnosticMessageImpl(
+                filePath: element.source!.fullName,
+                length: element.nameLength,
+                message: '$name is defined in ${element.source!.fullName}',
+                offset: element.nameOffset,
+                url: null));
+          }
 
-        arguments[typeToConvert.index] = buffer != null
-            ? '${typeToConvert.displayName} ($buffer)'
-            : typeToConvert.displayName;
+          if (buffer != null) {
+            arguments[typeToConvert.index] =
+                '${typeToConvert.displayName} ($buffer)';
+          } else {
+            arguments[typeToConvert.index] = typeToConvert.displayName;
+          }
+        }
       }
     }
     return messages;
@@ -451,22 +453,6 @@
   }
 }
 
-/// Used by [ErrorReporter._convertTypeNames] to keep track of an error argument
-/// that is an [Element], that is being converted to a display string.
-class _ElementToConvert implements _ToConvert {
-  @override
-  final int index;
-
-  @override
-  final String displayName;
-
-  @override
-  final Iterable<Element> allElements;
-
-  _ElementToConvert(this.index, Element element, this.displayName)
-      : allElements = [element];
-}
-
 /// An [AnalysisErrorListener] that ignores error.
 class _NullErrorListener implements AnalysisErrorListener {
   @override
@@ -475,61 +461,42 @@
   }
 }
 
-/// Used by [ErrorReporter._convertTypeNames] to keep track of an argument that
-/// is being converted to a display string.
-abstract class _ToConvert {
-  /// A list of all elements involved in the [DartType] or [Element]'s display
-  /// string.
-  Iterable<Element> get allElements;
-
-  /// The argument's display string, to replace the argument in the argument
-  /// list.
-  String get displayName;
-
-  /// The index of the argument in the argument list.
-  int get index;
-}
-
-/// Used by [ErrorReporter._convertTypeNames] to keep track of an error argument
-/// that is a [DartType], that is being converted to a display string.
-class _TypeToConvert implements _ToConvert {
-  @override
+/// Used by `ErrorReporter._convertTypeNames` to keep track of a type that is
+/// being converted.
+class _TypeToConvert {
   final int index;
-
-  final DartType _type;
-
-  @override
+  final DartType type;
   final String displayName;
 
-  @override
-  late final Iterable<Element> allElements = () {
-    var elements = <Element>{};
+  List<Element>? _allElements;
 
-    void addElementsFrom(DartType type) {
-      if (type is FunctionType) {
-        addElementsFrom(type.returnType);
-        for (var parameter in type.parameters) {
-          addElementsFrom(parameter.type);
-        }
-      } else if (type is RecordType) {
-        for (var parameter in type.fields) {
-          addElementsFrom(parameter.type);
-        }
-      } else if (type is InterfaceType) {
-        if (elements.add(type.element)) {
-          for (var typeArgument in type.typeArguments) {
-            addElementsFrom(typeArgument);
+  _TypeToConvert(this.index, this.type, this.displayName);
+
+  List<Element> allElements() {
+    if (_allElements == null) {
+      Set<Element> elements = <Element>{};
+
+      void addElementsFrom(DartType type) {
+        if (type is FunctionType) {
+          addElementsFrom(type.returnType);
+          for (ParameterElement parameter in type.parameters) {
+            addElementsFrom(parameter.type);
+          }
+        } else if (type is InterfaceType) {
+          if (elements.add(type.element)) {
+            for (DartType typeArgument in type.typeArguments) {
+              addElementsFrom(typeArgument);
+            }
           }
         }
       }
+
+      addElementsFrom(type);
+      _allElements = elements.where((element) {
+        var name = element.name;
+        return name != null && name.isNotEmpty;
+      }).toList();
     }
-
-    addElementsFrom(_type);
-    return elements.where((element) {
-      var name = element.name;
-      return name != null && name.isNotEmpty;
-    });
-  }();
-
-  _TypeToConvert(this.index, this._type, this.displayName);
+    return _allElements!;
+  }
 }
diff --git a/pkg/analyzer/lib/src/analysis_options/apply_options.dart b/pkg/analyzer/lib/src/analysis_options/apply_options.dart
index e266feb..6afa907 100644
--- a/pkg/analyzer/lib/src/analysis_options/apply_options.dart
+++ b/pkg/analyzer/lib/src/analysis_options/apply_options.dart
@@ -4,9 +4,11 @@
 
 import 'package:analyzer/dart/analysis/code_style_options.dart';
 import 'package:analyzer/dart/analysis/features.dart';
+import 'package:analyzer/dart/analysis/formatter_options.dart';
 import 'package:analyzer/error/error.dart';
 import 'package:analyzer/source/error_processor.dart';
 import 'package:analyzer/src/analysis_options/code_style_options.dart';
+import 'package:analyzer/src/analysis_options/formatter_options.dart';
 import 'package:analyzer/src/dart/analysis/experiments.dart';
 import 'package:analyzer/src/generated/engine.dart';
 import 'package:analyzer/src/generated/utilities_general.dart';
@@ -148,6 +150,18 @@
     return CodeStyleOptionsImpl(this, useFormatter: useFormatter);
   }
 
+  FormatterOptions buildFormatterOptions(YamlNode? formatter) {
+    int? pageWidth;
+    if (formatter is YamlMap) {
+      var formatNode = formatter.valueAt(AnalyzerOptions.pageWidth);
+      var formatValue = formatNode?.value;
+      if (formatValue is int && formatValue > 0) {
+        pageWidth = formatValue;
+      }
+    }
+    return FormatterOptionsImpl(this, pageWidth: pageWidth);
+  }
+
   void _applyLegacyPlugins(YamlNode? plugins) {
     var pluginName = plugins.stringValue;
     if (pluginName != null) {
@@ -226,6 +240,10 @@
     var codeStyle = optionMap.valueAt(AnalyzerOptions.codeStyle);
     codeStyleOptions = buildCodeStyleOptions(codeStyle);
 
+    // Process the 'formatter' option.
+    var formatter = optionMap.valueAt(AnalyzerOptions.formatter);
+    formatterOptions = buildFormatterOptions(formatter);
+
     var config = parseConfig(optionMap);
     if (config != null) {
       var enabledRules = Registry.ruleRegistry.enabled(config);
diff --git a/pkg/analyzer/lib/src/analysis_options/formatter_options.dart b/pkg/analyzer/lib/src/analysis_options/formatter_options.dart
new file mode 100644
index 0000000..e2352eb
--- /dev/null
+++ b/pkg/analyzer/lib/src/analysis_options/formatter_options.dart
@@ -0,0 +1,18 @@
+// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analyzer/dart/analysis/analysis_options.dart';
+import 'package:analyzer/dart/analysis/formatter_options.dart';
+
+/// The concrete implementation of [FormatterOptions].
+class FormatterOptionsImpl implements FormatterOptions {
+  /// The analysis options that owns this instance.
+  final AnalysisOptions options;
+
+  /// The width configured for where the formatter should wrap code.
+  @override
+  final int? pageWidth;
+
+  FormatterOptionsImpl(this.options, {this.pageWidth});
+}
diff --git a/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart b/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart
index 233e1e2..8041f93 100644
--- a/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart
@@ -12,7 +12,7 @@
 
 /// The current version of the Dart language (or, for non-stable releases, the
 /// version of the language currently in the process of being developed).
-const _currentVersion = '3.6.0';
+const _currentVersion = '3.7.0';
 
 /// A map containing information about all known experimental flags.
 final _knownFeatures = <String, ExperimentalFeature>{
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index 98a53a7..dd6f1ed 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -1553,8 +1553,8 @@
   ConstructorElement2 get baseElement => this;
 
   @override
-  Element2? get enclosingElement2 =>
-      (firstFragment._enclosingElement3 as InstanceFragment).element;
+  InterfaceElement2 get enclosingElement2 =>
+      (firstFragment._enclosingElement3 as InterfaceFragment).element;
 
   @override
   bool get isConst => firstFragment.isConst;
diff --git a/pkg/analyzer/lib/src/dart/element/member.dart b/pkg/analyzer/lib/src/dart/element/member.dart
index 795de17..c1d845d 100644
--- a/pkg/analyzer/lib/src/dart/element/member.dart
+++ b/pkg/analyzer/lib/src/dart/element/member.dart
@@ -55,6 +55,9 @@
   InterfaceElement get enclosingElement => declaration.enclosingElement;
 
   @override
+  InterfaceElement2 get enclosingElement2 => _element2.enclosingElement2;
+
+  @override
   InterfaceElement get enclosingElement3 => declaration.enclosingElement3;
 
   @override
diff --git a/pkg/analyzer/lib/src/dart/element/type_constraint_gatherer.dart b/pkg/analyzer/lib/src/dart/element/type_constraint_gatherer.dart
index b12474a..23b3a8e 100644
--- a/pkg/analyzer/lib/src/dart/element/type_constraint_gatherer.dart
+++ b/pkg/analyzer/lib/src/dart/element/type_constraint_gatherer.dart
@@ -124,9 +124,10 @@
 
   /// Tries to match [P] as a subtype for [Q].
   ///
-  /// If the match succeeds, the resulting type constraints are recorded for
-  /// later use by [computeConstraints].  If the match fails, the set of type
-  /// constraints is unchanged.
+  /// If [P] is a subtype of [Q] under some constraints, the constraints making
+  /// the relation possible are recorded to [_constraints], and `true` is
+  /// returned. Otherwise, [_constraints] is left unchanged (or rolled back),
+  /// and `false` is returned.
   bool trySubtypeMatch(DartType P, DartType Q, bool leftSchema,
       {required AstNode? nodeForTesting}) {
     // If `P` is `_` then the match holds with no constraints.
@@ -193,7 +194,6 @@
             nodeForTesting: nodeForTesting)) {
           return true;
         }
-        _constraints.length = rewind;
       }
 
       // Or if `P` is `dynamic` or `void` and `Object` is a subtype match
@@ -203,7 +203,6 @@
             nodeForTesting: nodeForTesting)) {
           return true;
         }
-        _constraints.length = rewind;
       }
 
       // Or if `P` is a subtype match for `Q0` under non-empty
@@ -213,14 +212,12 @@
       if (P_matches_Q0 && _constraints.length != rewind) {
         return true;
       }
-      _constraints.length = rewind;
 
       // Or if `P` is a subtype match for `Null` under constraint set `C`.
       if (trySubtypeMatch(P, _typeSystem.nullNone, leftSchema,
           nodeForTesting: nodeForTesting)) {
         return true;
       }
-      _constraints.length = rewind;
 
       // Or if `P` is a subtype match for `Q0` under empty
       // constraint set `C`.
@@ -295,13 +292,11 @@
     //   If `B` is a subtype match for `Q` with constraint set `C`.
     // Note: we have already eliminated the case that `X` is a variable in `L`.
     if (P_nullability == NullabilitySuffix.none && P is TypeParameterTypeImpl) {
-      var rewind = _constraints.length;
       var B = P.promotedBound ?? P.element.bound;
       if (B != null &&
           trySubtypeMatch(B, Q, leftSchema, nodeForTesting: nodeForTesting)) {
         return true;
       }
-      _constraints.length = rewind;
     }
 
     bool? result = performSubtypeConstraintGenerationForTypeDeclarationTypes(
@@ -369,6 +364,12 @@
     }
   }
 
+  /// Matches [P] against [Q], where [P] and [Q] are both function types.
+  ///
+  /// If [P] is a subtype of [Q] under some constraints, the constraints making
+  /// the relation possible are recorded to [_constraints], and `true` is
+  /// returned. Otherwise, [_constraints] is left unchanged (or rolled back),
+  /// and `false` is returned.
   bool _functionType(FunctionType P, FunctionType Q, bool leftSchema,
       {required AstNode? nodeForTesting}) {
     if (P.nullabilitySuffix != NullabilitySuffix.none) {
@@ -451,11 +452,18 @@
     return true;
   }
 
-  /// A function type `(M0,..., Mn, [M{n+1}, ..., Mm]) -> R0` is a subtype
-  /// match for a function type `(N0,..., Nk, [N{k+1}, ..., Nr]) -> R1` with
-  /// respect to `L` under constraints `C0 + ... + Cr + C`.
+  /// Matches [P] against [Q], where [P] and [Q] are both non-generic function
+  /// types.
+  ///
+  /// If [P] is a subtype of [Q] under some constraints, the constraints making
+  /// the relation possible are recorded to [_constraints], and `true` is
+  /// returned. Otherwise, [_constraints] is left unchanged (or rolled back),
+  /// and `false` is returned.
   bool _functionType0(FunctionType f, FunctionType g, bool leftSchema,
       {required AstNode? nodeForTesting}) {
+    // A function type `(M0,..., Mn, [M{n+1}, ..., Mm]) -> R0` is a subtype
+    // match for a function type `(N0,..., Nk, [N{k+1}, ..., Nr]) -> R1` with
+    // respect to `L` under constraints `C0 + ... + Cr + C`.
     bool? result = performSubtypeConstraintGenerationForFunctionTypes(f, g,
         leftSchema: leftSchema, astNodeForTesting: nodeForTesting);
     if (result != null) {
@@ -464,12 +472,18 @@
     return false;
   }
 
-  /// If `P` is `(M0, ..., Mk)` and `Q` is `(N0, ..., Nk)`, then the match
-  /// holds under constraints `C0 + ... + Ck`:
-  ///   If `Mi` is a subtype match for `Ni` with respect to L under
-  ///   constraints `Ci`.
+  /// Matches [P] against [Q], where [P] and [Q] are both record types.
+  ///
+  /// If [P] is a subtype of [Q] under some constraints, the constraints making
+  /// the relation possible are recorded to [_constraints], and `true` is
+  /// returned. Otherwise, [_constraints] is left unchanged (or rolled back),
+  /// and `false` is returned.
   bool _recordType(RecordTypeImpl P, RecordTypeImpl Q, bool leftSchema,
       {required AstNode? nodeForTesting}) {
+    // If `P` is `(M0, ..., Mk)` and `Q` is `(N0, ..., Nk)`, then the match
+    // holds under constraints `C0 + ... + Ck`:
+    //   If `Mi` is a subtype match for `Ni` with respect to L under
+    //   constraints `Ci`.
     if (P.nullabilitySuffix != NullabilitySuffix.none) {
       return false;
     }
diff --git a/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart
index 789b233..1f51888 100644
--- a/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart
@@ -117,33 +117,21 @@
     }
 
     // The most specific extension is ambiguous.
-    if (mostSpecific.length == 2) {
-      _errorReporter.atEntity(
-        nameEntity,
-        CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO,
-        arguments: [
-          name.name,
-          mostSpecific[0].extension,
-          mostSpecific[1].extension,
-        ],
-      );
-    } else {
-      _errorReporter.atEntity(
-        nameEntity,
-        CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_THREE_OR_MORE,
-        arguments: [
-          name.name,
-          mostSpecific.map((e) {
-            var name = e.extension.name;
-            if (name != null) {
-              return "extension '$name'";
-            }
-            var type = e.extension.extendedType.getDisplayString();
-            return "unnamed extension on '$type'";
-          }).commaSeparatedWithAnd,
-        ],
-      );
-    }
+    _errorReporter.atEntity(
+      nameEntity,
+      CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS,
+      arguments: [
+        name.name,
+        mostSpecific.map((e) {
+          var name = e.extension.name;
+          if (name != null) {
+            return "extension '$name'";
+          }
+          var type = e.extension.extendedType.getDisplayString();
+          return "unnamed extension on '$type'";
+        }).commaSeparatedWithAnd,
+      ],
+    );
     return ResolutionResult.ambiguous;
   }
 
diff --git a/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart b/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
index 15f28d2..219759e 100644
--- a/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/flow_analysis_visitor.dart
@@ -87,24 +87,34 @@
 
   final bool fieldPromotionEnabled;
 
+  final bool inferenceUpdate4Enabled;
+
   /// The current flow, when resolving a function body, or `null` otherwise.
   FlowAnalysis<AstNode, Statement, Expression, PromotableElement,
       SharedTypeView<DartType>>? flow;
 
   FlowAnalysisHelper(bool retainDataForTesting, FeatureSet featureSet,
       {required TypeSystemOperations typeSystemOperations})
-      : this._(typeSystemOperations,
-            retainDataForTesting ? FlowAnalysisDataForTesting() : null,
-            isNonNullableByDefault: featureSet.isEnabled(Feature.non_nullable),
-            respectImplicitlyTypedVarInitializers:
-                featureSet.isEnabled(Feature.constructor_tearoffs),
-            fieldPromotionEnabled:
-                featureSet.isEnabled(Feature.inference_update_2));
+      : this._(
+          typeSystemOperations,
+          retainDataForTesting ? FlowAnalysisDataForTesting() : null,
+          isNonNullableByDefault: featureSet.isEnabled(Feature.non_nullable),
+          respectImplicitlyTypedVarInitializers:
+              featureSet.isEnabled(Feature.constructor_tearoffs),
+          fieldPromotionEnabled:
+              featureSet.isEnabled(Feature.inference_update_2),
+          inferenceUpdate4Enabled:
+              featureSet.isEnabled(Feature.inference_update_4),
+        );
 
-  FlowAnalysisHelper._(this.typeOperations, this.dataForTesting,
-      {required this.isNonNullableByDefault,
-      required this.respectImplicitlyTypedVarInitializers,
-      required this.fieldPromotionEnabled});
+  FlowAnalysisHelper._(
+    this.typeOperations,
+    this.dataForTesting, {
+    required this.isNonNullableByDefault,
+    required this.respectImplicitlyTypedVarInitializers,
+    required this.fieldPromotionEnabled,
+    required this.inferenceUpdate4Enabled,
+  });
 
   LocalVariableTypeProvider get localVariableTypeProvider {
     return _LocalVariableTypeProvider(this);
@@ -261,10 +271,14 @@
     }
     flow = isNonNullableByDefault
         ? FlowAnalysis<AstNode, Statement, Expression, PromotableElement,
-                SharedTypeView<DartType>>(typeOperations, assignedVariables!,
+            SharedTypeView<DartType>>(
+            typeOperations,
+            assignedVariables!,
             respectImplicitlyTypedVarInitializers:
                 respectImplicitlyTypedVarInitializers,
-            fieldPromotionEnabled: fieldPromotionEnabled)
+            fieldPromotionEnabled: fieldPromotionEnabled,
+            inferenceUpdate4Enabled: inferenceUpdate4Enabled,
+          )
         : FlowAnalysis<AstNode, Statement, Expression, PromotableElement,
                 SharedTypeView<DartType>>.legacy(
             typeOperations, assignedVariables!);
diff --git a/pkg/analyzer/lib/src/error/codes.g.dart b/pkg/analyzer/lib/src/error/codes.g.dart
index ec12582..343e8e5 100644
--- a/pkg/analyzer/lib/src/error/codes.g.dart
+++ b/pkg/analyzer/lib/src/error/codes.g.dart
@@ -70,31 +70,14 @@
   ///  Parameters:
   ///  0: the name of the member
   ///  1: the names of the declaring extensions
-  static const CompileTimeErrorCode
-      AMBIGUOUS_EXTENSION_MEMBER_ACCESS_THREE_OR_MORE = CompileTimeErrorCode(
+  static const CompileTimeErrorCode AMBIGUOUS_EXTENSION_MEMBER_ACCESS =
+      CompileTimeErrorCode(
     'AMBIGUOUS_EXTENSION_MEMBER_ACCESS',
     "A member named '{0}' is defined in {1}, and none are more specific.",
     correctionMessage:
         "Try using an extension override to specify the extension you want to "
         "be chosen.",
     hasPublishedDocs: true,
-    uniqueName: 'AMBIGUOUS_EXTENSION_MEMBER_ACCESS_THREE_OR_MORE',
-  );
-
-  ///  Parameters:
-  ///  0: the name of the member
-  ///  1: the name of the first declaring extension
-  ///  2: the names of the second declaring extension
-  static const CompileTimeErrorCode AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO =
-      CompileTimeErrorCode(
-    'AMBIGUOUS_EXTENSION_MEMBER_ACCESS',
-    "A member named '{0}' is defined in '{1}' and '{2}', and neither is more "
-        "specific.",
-    correctionMessage:
-        "Try using an extension override to specify the extension you want to "
-        "be chosen.",
-    hasPublishedDocs: true,
-    uniqueName: 'AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO',
   );
 
   ///  Parameters:
diff --git a/pkg/analyzer/lib/src/error/error_code_values.g.dart b/pkg/analyzer/lib/src/error/error_code_values.g.dart
index 407af9ca..c207246 100644
--- a/pkg/analyzer/lib/src/error/error_code_values.g.dart
+++ b/pkg/analyzer/lib/src/error/error_code_values.g.dart
@@ -49,8 +49,7 @@
   CompileTimeErrorCode.ABSTRACT_FIELD_INITIALIZER,
   CompileTimeErrorCode.ABSTRACT_SUPER_MEMBER_REFERENCE,
   CompileTimeErrorCode.AMBIGUOUS_EXPORT,
-  CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_THREE_OR_MORE,
-  CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO,
+  CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS,
   CompileTimeErrorCode.AMBIGUOUS_IMPORT,
   CompileTimeErrorCode.AMBIGUOUS_SET_OR_MAP_LITERAL_BOTH,
   CompileTimeErrorCode.AMBIGUOUS_SET_OR_MAP_LITERAL_EITHER,
diff --git a/pkg/analyzer/lib/src/generated/engine.dart b/pkg/analyzer/lib/src/generated/engine.dart
index caf41d6..3950a30 100644
--- a/pkg/analyzer/lib/src/generated/engine.dart
+++ b/pkg/analyzer/lib/src/generated/engine.dart
@@ -9,6 +9,7 @@
 import 'package:analyzer/dart/analysis/code_style_options.dart';
 import 'package:analyzer/dart/analysis/declared_variables.dart';
 import 'package:analyzer/dart/analysis/features.dart';
+import 'package:analyzer/dart/analysis/formatter_options.dart';
 import 'package:analyzer/error/error.dart';
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/instrumentation/instrumentation.dart';
@@ -16,6 +17,7 @@
 import 'package:analyzer/source/line_info.dart';
 import 'package:analyzer/source/source.dart';
 import 'package:analyzer/src/analysis_options/code_style_options.dart';
+import 'package:analyzer/src/analysis_options/formatter_options.dart';
 import 'package:analyzer/src/dart/analysis/experiments.dart';
 import 'package:analyzer/src/generated/source.dart' show SourceFactory;
 import 'package:analyzer/src/lint/linter.dart';
@@ -239,6 +241,9 @@
   @override
   late CodeStyleOptions codeStyleOptions;
 
+  @override
+  late FormatterOptions formatterOptions;
+
   /// The set of "un-ignorable" error names, as parsed in [AnalyzerOptions] from
   /// an analysis options file.
   Set<String> unignorableNames = {};
@@ -247,12 +252,14 @@
   /// values.
   AnalysisOptionsImpl({this.file}) {
     codeStyleOptions = CodeStyleOptionsImpl(this, useFormatter: false);
+    formatterOptions = FormatterOptionsImpl(this);
   }
 
   /// Initialize a newly created set of analysis options to have the same values
   /// as those in the given set of analysis [options].
   AnalysisOptionsImpl.from(AnalysisOptions options) {
     codeStyleOptions = options.codeStyleOptions;
+    formatterOptions = options.formatterOptions;
     contextFeatures = options.contextFeatures;
     enabledLegacyPluginNames = options.enabledLegacyPluginNames;
     errorProcessors = options.errorProcessors;
diff --git a/pkg/analyzer/lib/src/task/options.dart b/pkg/analyzer/lib/src/task/options.dart
index 8b592db..bf87e60 100644
--- a/pkg/analyzer/lib/src/task/options.dart
+++ b/pkg/analyzer/lib/src/task/options.dart
@@ -239,6 +239,7 @@
   static const String enableExperiment = 'enable-experiment';
   static const String errors = 'errors';
   static const String exclude = 'exclude';
+  static const String formatter = 'formatter';
   static const String include = 'include';
   static const String language = 'language';
   static const String optionalChecks = 'optional-checks';
@@ -270,6 +271,9 @@
   /// Ways to say `include`.
   static const List<String> includeSynonyms = ['include', 'true'];
 
+  // Formatter options.
+  static const String pageWidth = 'page_width';
+
   static const String propagateLinterExceptions = 'propagate-linter-exceptions';
 
   /// Ways to say `true` or `false`.
@@ -648,6 +652,52 @@
   }
 }
 
+/// Validates `formatter` options.
+class FormatterOptionsValidator extends OptionsValidator {
+  @override
+  void validate(ErrorReporter reporter, YamlMap options) {
+    var formatter = options.valueAt(AnalyzerOptions.formatter);
+    if (formatter is YamlMap) {
+      for (var MapEntry(key: keyNode, value: valueNode)
+          in formatter.nodeMap.entries) {
+        if (keyNode.value == AnalyzerOptions.pageWidth) {
+          _validatePageWidth(keyNode, valueNode, reporter);
+        } else {
+          reporter.atSourceSpan(
+            keyNode.span,
+            AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITHOUT_VALUES,
+            arguments: [
+              AnalyzerOptions.formatter,
+              keyNode.toString(),
+            ],
+          );
+        }
+      }
+    } else if (formatter != null && formatter.value != null) {
+      reporter.atSourceSpan(
+        formatter.span,
+        AnalysisOptionsWarningCode.INVALID_SECTION_FORMAT,
+        arguments: [AnalyzerOptions.formatter],
+      );
+    }
+  }
+
+  void _validatePageWidth(
+      YamlNode keyNode, YamlNode valueNode, ErrorReporter reporter) {
+    var value = valueNode.value;
+    if (value is! int || value <= 0) {
+      reporter.atSourceSpan(
+        valueNode.span,
+        AnalysisOptionsWarningCode.INVALID_OPTION,
+        arguments: [
+          keyNode.toString(),
+          '"page_width" must be a positive integer.',
+        ],
+      );
+    }
+  }
+}
+
 /// Validates `analyzer` language configuration options.
 class LanguageOptionValidator extends OptionsValidator {
   final ErrorBuilder _builder = ErrorBuilder(AnalyzerOptions.languageOptions);
@@ -776,6 +826,7 @@
   }) : _validators = [
           AnalyzerOptionsValidator(),
           CodeStyleOptionsValidator(),
+          FormatterOptionsValidator(),
           LinterOptionsValidator(),
           LinterRuleOptionsValidator(
             provider: provider,
diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml
index 1ead2a7..15aeb69d 100644
--- a/pkg/analyzer/messages.yaml
+++ b/pkg/analyzer/messages.yaml
@@ -408,16 +408,14 @@
       export 'a.dart';
       export 'b.dart' hide C;
       ```
-  AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO:
-    sharedName: AMBIGUOUS_EXTENSION_MEMBER_ACCESS
-    problemMessage: "A member named '{0}' is defined in '{1}' and '{2}', and neither is more specific."
+  AMBIGUOUS_EXTENSION_MEMBER_ACCESS:
+    problemMessage: "A member named '{0}' is defined in {1}, and none are more specific."
     correctionMessage: Try using an extension override to specify the extension you want to be chosen.
     hasPublishedDocs: true
     comment: |-
       Parameters:
       0: the name of the member
-      1: the name of the first declaring extension
-      2: the names of the second declaring extension
+      1: the names of the declaring extensions
     documentation: |-
       #### Description
 
@@ -473,15 +471,6 @@
         print(E2(s).charCount);
       }
       ```
-  AMBIGUOUS_EXTENSION_MEMBER_ACCESS_THREE_OR_MORE:
-    sharedName: AMBIGUOUS_EXTENSION_MEMBER_ACCESS
-    problemMessage: "A member named '{0}' is defined in {1}, and none are more specific."
-    correctionMessage: Try using an extension override to specify the extension you want to be chosen.
-    hasPublishedDocs: true
-    comment: |-
-      Parameters:
-      0: the name of the member
-      1: the names of the declaring extensions
   AMBIGUOUS_IMPORT:
     problemMessage: "The name '{0}' is defined in the libraries {1}."
     correctionMessage: "Try using 'as prefix' for one of the import directives, or hiding the name from all but one of the imports."
diff --git a/pkg/analyzer/pubspec.yaml b/pkg/analyzer/pubspec.yaml
index fcde8f7..39a770e 100644
--- a/pkg/analyzer/pubspec.yaml
+++ b/pkg/analyzer/pubspec.yaml
@@ -1,5 +1,5 @@
 name: analyzer
-version: 6.10.0-dev
+version: 6.10.0
 description: >-
   This package provides a library that performs static analysis of Dart code.
 repository: https://github.com/dart-lang/sdk/tree/main/pkg/analyzer
@@ -8,7 +8,7 @@
   sdk: '>=3.3.0 <4.0.0'
 
 dependencies:
-  _fe_analyzer_shared: ^74.0.0
+  _fe_analyzer_shared: ^75.0.0
   collection: ^1.19.0
   convert: ^3.0.0
   crypto: ^3.0.0
diff --git a/pkg/analyzer/test/src/diagnostics/ambiguous_extension_member_access_test.dart b/pkg/analyzer/test/src/diagnostics/ambiguous_extension_member_access_test.dart
index 7995377..c96edd7 100644
--- a/pkg/analyzer/test/src/diagnostics/ambiguous_extension_member_access_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/ambiguous_extension_member_access_test.dart
@@ -29,7 +29,7 @@
 
 int f(A a) => a();
 ''', [
-      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 110, 1),
+      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 110, 1),
     ]);
   }
 
@@ -47,7 +47,7 @@
   0.a;
 }
 ''', [
-      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 98, 1),
+      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 98, 1),
     ]);
 
     var node = findNode.propertyAccess('0.a');
@@ -111,7 +111,7 @@
   0.a;
 }
 ''', [
-      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 91, 1),
+      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 91, 1),
     ]);
 
     var node = findNode.propertyAccess('0.a');
@@ -144,7 +144,7 @@
   0.a;
 }
 ''', [
-      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 96, 1),
+      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 96, 1),
     ]);
 
     var node = findNode.propertyAccess('0.a');
@@ -172,38 +172,8 @@
   0.foo();
 }
 ''', [
-      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 129, 3,
-          messageContains: [
-            "in 'extension E1 on int' and 'extension E2 on int',",
-          ]),
-    ]);
-  }
-
-  test_method_conflict_conflict_notSpecific_sameName() async {
-    var one = newFile('$testPackageLibPath/one.dart', '''
-extension E on int { void foo() {} }
-''');
-    var two = newFile('$testPackageLibPath/two.dart', '''
-extension E on int { void foo() {} }
-''');
-    await assertErrorsInCode('''
-// ignore_for_file: unused_import
-import 'one.dart';
-import 'two.dart';
-void f() {
-  0.foo();
-}
-''', [
-      error(
-        CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO,
-        87,
-        3,
-        messageContains: [
-          "'extension E on int (where E is defined in ${one.path})' and "
-              "'extension E on int (where E is defined in ${two.path})',",
-        ],
-        contextMessages: [message(one, 10, 1), message(two, 10, 1)],
-      ),
+      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 129, 3,
+          messageContains: ["in extension 'E1' and extension 'E2',"]),
     ]);
   }
 
@@ -227,10 +197,8 @@
   0.foo();
 }
 ''', [
-      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 129, 3,
-          messageContains: [
-            "in 'extension E1 on int' and 'extension E2 on int',",
-          ]),
+      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 129, 3,
+          messageContains: ["in extension 'E1' and extension 'E2',"]),
     ]);
   }
 
@@ -259,7 +227,7 @@
   0.a();
 }
 ''', [
-      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 88, 1),
+      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 88, 1),
     ]);
 
     var node = findNode.methodInvocation('0.a()');
@@ -291,10 +259,8 @@
   0.foo();
 }
 ''', [
-      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 129, 3,
-          messageContains: [
-            "in 'extension E1 on int' and 'extension E2 on int',"
-          ]),
+      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 129, 3,
+          messageContains: ["in extension 'E1' and extension 'E2',"]),
     ]);
   }
 
@@ -308,10 +274,7 @@
   0.foo();
 }
 ''', [
-      error(
-          CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_THREE_OR_MORE,
-          167,
-          3,
+      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 167, 3,
           messageContains: [
             "in extension 'E1', extension 'E2', and extension 'E3',"
           ]),
@@ -350,7 +313,7 @@
   t.foo;
 }
 ''', [
-      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 396, 3),
+      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 396, 3),
     ]);
   }
 
@@ -368,7 +331,7 @@
 
 A f(A a) => a + a;
 ''', [
-      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 122, 5),
+      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 122, 5),
     ]);
   }
 
@@ -388,7 +351,7 @@
   a += 0;
 }
 ''', [
-      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 130, 2),
+      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 130, 2),
     ]);
   }
 
@@ -406,7 +369,7 @@
 
 int f(A a) => a[0];
 ''', [
-      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 134, 1),
+      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 134, 1),
     ]);
   }
 
@@ -424,7 +387,7 @@
   0[1] += 2;
 }
 ''', [
-      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 136, 1),
+      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 136, 1),
     ]);
   }
 
@@ -442,7 +405,7 @@
 
 int f(A a) => -a;
 ''', [
-      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 123, 1),
+      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 123, 1),
     ]);
   }
 
@@ -460,7 +423,7 @@
   0.a = 3;
 }
 ''', [
-      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 88, 1),
+      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 88, 1),
     ]);
 
     assertResolvedNodeText(findNode.assignment('= 3'), r'''
@@ -513,9 +476,10 @@
 int g(List<A> x) => x();
 int h(List<B> x) => x();
 ''', [
-      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS_TWO, 167, 1,
+      error(CompileTimeErrorCode.AMBIGUOUS_EXTENSION_MEMBER_ACCESS, 167, 1,
           messageContains: [
-            "'extension on List<A>' and 'extension on List<B>',"
+            "in unnamed extension on 'List<A>' and unnamed extension on "
+                "'List<B>',"
           ]),
     ]);
   }
diff --git a/pkg/analyzer/test/src/task/options_test.dart b/pkg/analyzer/test/src/task/options_test.dart
index 0b0524f..956fac7 100644
--- a/pkg/analyzer/test/src/task/options_test.dart
+++ b/pkg/analyzer/test/src/task/options_test.dart
@@ -332,6 +332,73 @@
 ''', [AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITHOUT_VALUES]);
   }
 
+  test_formatter_invalid_key() {
+    validate('''
+formatter:
+  wrong: 123
+''', [
+      AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITHOUT_VALUES,
+    ]);
+  }
+
+  test_formatter_invalid_keys() {
+    validate('''
+formatter:
+  wrong: 123
+  wrong2: 123
+''', [
+      AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITHOUT_VALUES,
+      AnalysisOptionsWarningCode.UNSUPPORTED_OPTION_WITHOUT_VALUES,
+    ]);
+  }
+
+  test_formatter_pageWidth_invalid_decimal() {
+    validate('''
+formatter:
+  page_width: 123.45
+''', [
+      AnalysisOptionsWarningCode.INVALID_OPTION,
+    ]);
+  }
+
+  test_formatter_pageWidth_invalid_negativeInteger() {
+    validate('''
+formatter:
+  page_width: -123
+''', [
+      AnalysisOptionsWarningCode.INVALID_OPTION,
+    ]);
+  }
+
+  test_formatter_pageWidth_invalid_string() {
+    validate('''
+formatter:
+  page_width: "123"
+''', [AnalysisOptionsWarningCode.INVALID_OPTION]);
+  }
+
+  test_formatter_pageWidth_invalid_zero() {
+    validate('''
+formatter:
+  page_width: 0
+''', [
+      AnalysisOptionsWarningCode.INVALID_OPTION,
+    ]);
+  }
+
+  test_formatter_pageWidth_valid_integer() {
+    validate('''
+formatter:
+  page_width: 123
+''', []);
+  }
+
+  test_formatter_valid_empty() {
+    validate('''
+formatter:
+''', []);
+  }
+
   test_linter_supported_rules() {
     Registry.ruleRegistry.register(TestRule());
     validate('''
diff --git a/pkg/analyzer/tool/diagnostics/diagnostics.md b/pkg/analyzer/tool/diagnostics/diagnostics.md
index 9062916..e5611dc 100644
--- a/pkg/analyzer/tool/diagnostics/diagnostics.md
+++ b/pkg/analyzer/tool/diagnostics/diagnostics.md
@@ -430,9 +430,6 @@
 
 ### ambiguous_extension_member_access
 
-_A member named '{0}' is defined in '{1}' and '{2}', and neither is more
-specific._
-
 _A member named '{0}' is defined in {1}, and none are more specific._
 
 #### Description
diff --git a/pkg/dart2wasm/tool/compile_benchmark b/pkg/dart2wasm/tool/compile_benchmark
index 02e5d79..eefae07 100755
--- a/pkg/dart2wasm/tool/compile_benchmark
+++ b/pkg/dart2wasm/tool/compile_benchmark
@@ -126,6 +126,12 @@
       shift
       ;;
 
+    --extra-compiler-option=--no-js-compatibility)
+      PLATFORM_FILENAME="$BIN_DIR/dart2wasm_platform.dill"
+      DART2WASM_ARGS+=("--no-js-compatibility")
+      shift
+      ;;
+
     --extra-compiler-option=*)
       DART2WASM_ARGS+=(${1#--extra-compiler-option=})
       shift
diff --git a/pkg/dartdev/lib/src/commands/compile.dart b/pkg/dartdev/lib/src/commands/compile.dart
index c8cc3f2..23ad8d6 100644
--- a/pkg/dartdev/lib/src/commands/compile.dart
+++ b/pkg/dartdev/lib/src/commands/compile.dart
@@ -4,7 +4,6 @@
 
 import 'dart:async';
 import 'dart:io';
-import 'dart:isolate';
 
 import 'package:args/args.dart';
 import 'package:dart2native/generate.dart';
@@ -88,48 +87,41 @@
 
   @override
   FutureOr<int> run() async {
-    if (!Sdk.checkArtifactExists(sdk.dart2jsSnapshot) ||
-        !Sdk.checkArtifactExists(sdk.librariesJson)) {
-      return 255;
+    if (!Sdk.checkArtifactExists(sdk.librariesJson)) {
+      return genericErrorExitCode;
     }
-
     final args = argResults!;
-
-    // Build arguments.
-    final buildArgs = <String>[
+    var snapshot = sdk.dart2jsAotSnapshot;
+    var runtime = sdk.dartAotRuntime;
+    if (!Sdk.checkArtifactExists(snapshot, logError: false)) {
+      // AOT snapshots cannot be generated on IA32, so we need this fallback
+      // branch until support for IA32 is dropped (https://dartbug.com/49969).
+      snapshot = sdk.dart2jsSnapshot;
+      runtime = sdk.dart;
+      if (!Sdk.checkArtifactExists(snapshot)) {
+        return genericErrorExitCode;
+      }
+    }
+    final dart2jsCommand = [
+      runtime,
+      snapshot,
       '--libraries-spec=${sdk.librariesJson}',
       '--cfe-invocation-modes=compile',
       '--invoker=dart_cli',
       // Add the remaining arguments.
       if (args.rest.isNotEmpty) ...args.rest.sublist(0),
     ];
-
-    var retval = 0;
-    final result = Completer<int>();
-    final exitPort = ReceivePort()
-      ..listen((msg) {
-        result.complete(0);
-      });
-    final errorPort = ReceivePort()
-      ..listen((error) {
-        log.stderr(error.toString());
-        result.complete(255);
-      });
     try {
-      await Isolate.spawnUri(Uri.file(sdk.dart2jsSnapshot), buildArgs, null,
-          onExit: exitPort.sendPort, onError: errorPort.sendPort);
-      retval = await result.future;
+      final exitCode = await runProcessInheritStdio(dart2jsCommand);
+      return exitCode;
     } catch (e, st) {
       log.stderr('Error: JS compilation failed');
       log.stderr(e.toString());
       if (verbose) {
         log.stderr(st.toString());
       }
-      retval = compileErrorExitCode;
+      return compileErrorExitCode;
     }
-    errorPort.close();
-    exitPort.close();
-    return retval;
   }
 }
 
diff --git a/pkg/dartdev/lib/src/core.dart b/pkg/dartdev/lib/src/core.dart
index c6a8c79..8e89b55 100644
--- a/pkg/dartdev/lib/src/core.dart
+++ b/pkg/dartdev/lib/src/core.dart
@@ -119,8 +119,11 @@
   }
 
   log.trace(command.join(' '));
-  final process = await Process.start(command.first, command.skip(1).toList(),
-      workingDirectory: cwd);
+  final process = await Process.start(
+    command.first,
+    command.skip(1).toList(),
+    workingDirectory: cwd,
+  );
   final (_, _, exitCode) = await (
     forward(process.stdout, false),
     forward(process.stderr, true),
@@ -129,6 +132,22 @@
   return exitCode;
 }
 
+Future<int> runProcessInheritStdio(
+  List<String> command, {
+  bool logToTrace = false,
+  void Function(String str)? listener,
+  String? cwd,
+}) async {
+  log.trace(command.join(' '));
+  final process = await Process.start(
+    command.first,
+    command.skip(1).toList(),
+    workingDirectory: cwd,
+    mode: ProcessStartMode.inheritStdio,
+  );
+  return await process.exitCode;
+}
+
 Future _streamLineTransform(
   Stream<List<int>> stream,
   Function(String line) handler,
diff --git a/pkg/dartdev/lib/src/sdk.dart b/pkg/dartdev/lib/src/sdk.dart
index 168294b..5ef7626 100644
--- a/pkg/dartdev/lib/src/sdk.dart
+++ b/pkg/dartdev/lib/src/sdk.dart
@@ -71,6 +71,10 @@
         'dart2js.dart.snapshot',
       );
 
+  String get dart2jsAotSnapshot => _snapshotPathFor(
+        'dart2js_aot.dart.snapshot',
+      );
+
   String get dart2wasmSnapshot => _snapshotPathFor(
         'dart2wasm_product.snapshot',
       );
diff --git a/pkg/dartdev/test/sdk_test.dart b/pkg/dartdev/test/sdk_test.dart
index de39966..9ef7801 100644
--- a/pkg/dartdev/test/sdk_test.dart
+++ b/pkg/dartdev/test/sdk_test.dart
@@ -30,7 +30,7 @@
   });
 
   test('dart2js snapshot', () {
-    expectFileExists(Sdk().dart2jsSnapshot);
+    expectSnapshotExists(Sdk().dart2jsAotSnapshot, Sdk().dart2jsSnapshot);
   });
 }
 
diff --git a/pkg/dev_compiler/lib/ddc.dart b/pkg/dev_compiler/lib/ddc.dart
index fb4364f..3526364 100755
--- a/pkg/dev_compiler/lib/ddc.dart
+++ b/pkg/dev_compiler/lib/ddc.dart
@@ -18,7 +18,6 @@
 import 'src/command/arguments.dart';
 import 'src/command/command.dart';
 import 'src/command/result.dart';
-import 'src/compiler/shared_command.dart';
 import 'src/kernel/expression_compiler_worker.dart';
 
 /// The internal entry point for the Dart Dev Compiler.
diff --git a/pkg/dev_compiler/lib/src/command/command.dart b/pkg/dev_compiler/lib/src/command/command.dart
index 76e7c82..b1a3018 100644
--- a/pkg/dev_compiler/lib/src/command/command.dart
+++ b/pkg/dev_compiler/lib/src/command/command.dart
@@ -8,6 +8,8 @@
 
 import 'package:args/args.dart';
 import 'package:build_integration/file_system/multi_root.dart';
+import 'package:front_end/src/api_prototype/macros.dart' as macros
+    show isMacroLibraryUri;
 import 'package:front_end/src/api_unstable/ddc.dart' as fe;
 import 'package:kernel/binary/ast_to_binary.dart' as kernel show BinaryPrinter;
 import 'package:kernel/class_hierarchy.dart';
@@ -21,7 +23,6 @@
 
 import '../compiler/js_names.dart' as js_ast;
 import '../compiler/module_builder.dart';
-import '../compiler/shared_command.dart';
 import '../js_ast/js_ast.dart' as js_ast;
 import '../js_ast/js_ast.dart' show js;
 import '../js_ast/source_map_printer.dart' show SourceMapPrintingContext;
@@ -930,3 +931,112 @@
     return previousValue;
   });
 }
+
+/// Convert a [source] string to a Uri, where the source may be a
+/// dart/file/package URI or a local win/mac/linux path.
+Uri sourcePathToUri(String source, {bool? windows}) {
+  if (windows == null) {
+    // Running on the web the Platform check will fail, and we can't use
+    // fromEnvironment because internally it's set to true for dart.library.io.
+    // So just catch the exception and if it fails then we're definitely not on
+    // Windows.
+    try {
+      windows = Platform.isWindows;
+    } catch (e) {
+      windows = false;
+    }
+  }
+  if (windows) {
+    source = source.replaceAll('\\', '/');
+  }
+
+  var result = Uri.base.resolve(source);
+  if (windows && result.scheme.length == 1) {
+    // Assume c: or similar --- interpret as file path.
+    return Uri.file(source, windows: true);
+  }
+  return result;
+}
+
+Uri sourcePathToRelativeUri(String source, {bool? windows}) {
+  var uri = sourcePathToUri(source, windows: windows);
+  if (uri.isScheme('file')) {
+    var uriPath = uri.path;
+    var root = Uri.base.path;
+    if (uriPath.startsWith(root)) {
+      return p.toUri(uriPath.substring(root.length));
+    }
+  }
+  return uri;
+}
+
+/// Adjusts the source uris in [sourceMap] to be relative uris, and returns
+/// the new map.
+///
+/// Source uris show up in two forms, absolute `file:` uris and custom
+/// [multiRootScheme] uris (also "absolute" uris, but always relative to some
+/// multi-root).
+///
+/// - `file:` uris are converted to be relative to [sourceMapBase], which
+///   defaults to the dirname of [sourceMapPath] if not provided.
+///
+/// - [multiRootScheme] uris are prefixed by [multiRootOutputPath]. If the
+///   path starts with `/lib`, then we strip that before making it relative
+///   to the [multiRootOutputPath], and assert that [multiRootOutputPath]
+///   starts with `/packages` (more explanation inline).
+///
+// TODO(#40251): Remove this logic from dev_compiler itself, push it to the
+// invokers of dev_compiler which have more knowledge about how they want
+// source paths to look.
+Map<String, Object?> placeSourceMap(Map<String, Object?> sourceMap,
+    String sourceMapPath, String? multiRootScheme,
+    {String? multiRootOutputPath, String? sourceMapBase}) {
+  var map = Map.of(sourceMap);
+  // Convert to a local file path if it's not.
+  sourceMapPath = sourcePathToUri(p.absolute(p.fromUri(sourceMapPath))).path;
+  var sourceMapDir = p.url.dirname(sourceMapPath);
+  sourceMapBase ??= sourceMapDir;
+  var list = (map['sources'] as List).toList();
+
+  String makeRelative(String sourcePath) {
+    var uri = sourcePathToUri(sourcePath);
+    var scheme = uri.scheme;
+    if (scheme == 'dart' || scheme == 'package' || scheme == multiRootScheme) {
+      if (scheme == multiRootScheme) {
+        // TODO(sigmund): extract all source-map normalization outside ddc. This
+        // custom logic is BUILD specific and could be shared with other tools
+        // like dart2js.
+        var shortPath = uri.path.replaceAll('/sdk/', '/dart-sdk/');
+        var multiRootPath = "${multiRootOutputPath ?? ''}$shortPath";
+        multiRootPath = p.url
+            .joinAll(p.split(p.relative(multiRootPath, from: sourceMapDir)));
+        return multiRootPath;
+      }
+      return sourcePath;
+    }
+
+    if (macros.isMacroLibraryUri(uri)) {
+      // TODO: https://github.com/dart-lang/sdk/issues/53913
+      return sourcePath;
+    }
+
+    if (uri.isScheme('http')) return sourcePath;
+
+    // Convert to a local file path if it's not.
+    sourcePath = sourcePathToUri(p.absolute(p.fromUri(uri))).path;
+
+    // Fall back to a relative path against the source map itself.
+    sourcePath = p.url.relative(sourcePath, from: sourceMapBase);
+
+    // Convert from relative local path to relative URI.
+    return p.toUri(sourcePath).path;
+  }
+
+  for (var i = 0; i < list.length; i++) {
+    list[i] = makeRelative(list[i] as String);
+  }
+  map['sources'] = list;
+  map['file'] =
+      map['file'] != null ? makeRelative(map['file'] as String) : null;
+  return map;
+}
diff --git a/pkg/dev_compiler/lib/src/compiler/shared_command.dart b/pkg/dev_compiler/lib/src/compiler/shared_command.dart
deleted file mode 100644
index cc6595d..0000000
--- a/pkg/dev_compiler/lib/src/compiler/shared_command.dart
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import 'dart:io';
-
-import 'package:front_end/src/api_prototype/macros.dart' as macros
-    show isMacroLibraryUri;
-import 'package:path/path.dart' as p;
-
-// TODO(nshahan) Merge all of this file the locations where they are used in
-// the kernel (only) version of DDC.
-
-/// Convert a [source] string to a Uri, where the source may be a
-/// dart/file/package URI or a local win/mac/linux path.
-Uri sourcePathToUri(String source, {bool? windows}) {
-  if (windows == null) {
-    // Running on the web the Platform check will fail, and we can't use
-    // fromEnvironment because internally it's set to true for dart.library.io.
-    // So just catch the exception and if it fails then we're definitely not on
-    // Windows.
-    try {
-      windows = Platform.isWindows;
-    } catch (e) {
-      windows = false;
-    }
-  }
-  if (windows) {
-    source = source.replaceAll('\\', '/');
-  }
-
-  var result = Uri.base.resolve(source);
-  if (windows && result.scheme.length == 1) {
-    // Assume c: or similar --- interpret as file path.
-    return Uri.file(source, windows: true);
-  }
-  return result;
-}
-
-Uri sourcePathToRelativeUri(String source, {bool? windows}) {
-  var uri = sourcePathToUri(source, windows: windows);
-  if (uri.isScheme('file')) {
-    var uriPath = uri.path;
-    var root = Uri.base.path;
-    if (uriPath.startsWith(root)) {
-      return p.toUri(uriPath.substring(root.length));
-    }
-  }
-  return uri;
-}
-
-/// Adjusts the source uris in [sourceMap] to be relative uris, and returns
-/// the new map.
-///
-/// Source uris show up in two forms, absolute `file:` uris and custom
-/// [multiRootScheme] uris (also "absolute" uris, but always relative to some
-/// multi-root).
-///
-/// - `file:` uris are converted to be relative to [sourceMapBase], which
-///   defaults to the dirname of [sourceMapPath] if not provided.
-///
-/// - [multiRootScheme] uris are prefixed by [multiRootOutputPath]. If the
-///   path starts with `/lib`, then we strip that before making it relative
-///   to the [multiRootOutputPath], and assert that [multiRootOutputPath]
-///   starts with `/packages` (more explanation inline).
-///
-// TODO(#40251): Remove this logic from dev_compiler itself, push it to the
-// invokers of dev_compiler which have more knowledge about how they want
-// source paths to look.
-Map<String, Object?> placeSourceMap(Map<String, Object?> sourceMap,
-    String sourceMapPath, String? multiRootScheme,
-    {String? multiRootOutputPath, String? sourceMapBase}) {
-  var map = Map.of(sourceMap);
-  // Convert to a local file path if it's not.
-  sourceMapPath = sourcePathToUri(p.absolute(p.fromUri(sourceMapPath))).path;
-  var sourceMapDir = p.url.dirname(sourceMapPath);
-  sourceMapBase ??= sourceMapDir;
-  var list = (map['sources'] as List).toList();
-
-  String makeRelative(String sourcePath) {
-    var uri = sourcePathToUri(sourcePath);
-    var scheme = uri.scheme;
-    if (scheme == 'dart' || scheme == 'package' || scheme == multiRootScheme) {
-      if (scheme == multiRootScheme) {
-        // TODO(sigmund): extract all source-map normalization outside ddc. This
-        // custom logic is BUILD specific and could be shared with other tools
-        // like dart2js.
-        var shortPath = uri.path.replaceAll('/sdk/', '/dart-sdk/');
-        var multiRootPath = "${multiRootOutputPath ?? ''}$shortPath";
-        multiRootPath = p.url
-            .joinAll(p.split(p.relative(multiRootPath, from: sourceMapDir)));
-        return multiRootPath;
-      }
-      return sourcePath;
-    }
-
-    if (macros.isMacroLibraryUri(uri)) {
-      // TODO: https://github.com/dart-lang/sdk/issues/53913
-      return sourcePath;
-    }
-
-    if (uri.isScheme('http')) return sourcePath;
-
-    // Convert to a local file path if it's not.
-    sourcePath = sourcePathToUri(p.absolute(p.fromUri(uri))).path;
-
-    // Fall back to a relative path against the source map itself.
-    sourcePath = p.url.relative(sourcePath, from: sourceMapBase);
-
-    // Convert from relative local path to relative URI.
-    return p.toUri(sourcePath).path;
-  }
-
-  for (var i = 0; i < list.length; i++) {
-    list[i] = makeRelative(list[i] as String);
-  }
-  map['sources'] = list;
-  map['file'] =
-      map['file'] != null ? makeRelative(map['file'] as String) : null;
-  return map;
-}
diff --git a/pkg/dev_compiler/test/memory_compiler.dart b/pkg/dev_compiler/test/memory_compiler.dart
index 5423cb8..ff7a177 100644
--- a/pkg/dev_compiler/test/memory_compiler.dart
+++ b/pkg/dev_compiler/test/memory_compiler.dart
@@ -4,7 +4,6 @@
 
 import 'package:dev_compiler/dev_compiler.dart';
 import 'package:dev_compiler/src/command/command.dart';
-import 'package:dev_compiler/src/compiler/shared_command.dart';
 import 'package:dev_compiler/src/js_ast/js_ast.dart';
 import 'package:front_end/src/api_unstable/ddc.dart' as fe;
 import 'package:kernel/target/targets.dart';
diff --git a/pkg/dev_compiler/test/string_to_uri_test.dart b/pkg/dev_compiler/test/string_to_uri_test.dart
index 9534711..8213e42 100755
--- a/pkg/dev_compiler/test/string_to_uri_test.dart
+++ b/pkg/dev_compiler/test/string_to_uri_test.dart
@@ -4,7 +4,7 @@
 
 import 'dart:io';
 
-import 'package:dev_compiler/src/compiler/shared_command.dart';
+import 'package:dev_compiler/src/command/command.dart';
 import 'package:test/test.dart';
 
 void main(List<String> args) {
diff --git a/pkg/front_end/lib/src/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/type_inference/type_inferrer.dart
index d98fcc4..96e174d 100644
--- a/pkg/front_end/lib/src/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/type_inference/type_inferrer.dart
@@ -91,7 +91,9 @@
           respectImplicitlyTypedVarInitializers:
               libraryBuilder.libraryFeatures.constructorTearoffs.isEnabled,
           fieldPromotionEnabled:
-              libraryBuilder.libraryFeatures.inferenceUpdate2.isEnabled);
+              libraryBuilder.libraryFeatures.inferenceUpdate2.isEnabled,
+          inferenceUpdate4Enabled:
+              libraryBuilder.libraryFeatures.inferenceUpdate4.isEnabled);
 
   @override
   final AssignedVariables<TreeNode, VariableDeclaration> assignedVariables;
diff --git a/pkg/json/pubspec.yaml b/pkg/json/pubspec.yaml
index 98a28ee..d58a51b 100644
--- a/pkg/json/pubspec.yaml
+++ b/pkg/json/pubspec.yaml
@@ -7,7 +7,7 @@
 repository: https://github.com/dart-lang/sdk/tree/main/pkg/json
 version: 0.20.2
 environment:
-  sdk: ^3.6.0-edge
+  sdk: ^3.7.0-edge
 dependencies:
   macros: ^0.1.0-main.5
 dev_dependencies:
diff --git a/pkg/kernel/lib/default_language_version.dart b/pkg/kernel/lib/default_language_version.dart
index c5a8085..5fa54ec 100644
--- a/pkg/kernel/lib/default_language_version.dart
+++ b/pkg/kernel/lib/default_language_version.dart
@@ -9,4 +9,4 @@
 
 import "ast.dart";
 
-const Version defaultLanguageVersion = const Version(3, 6);
+const Version defaultLanguageVersion = const Version(3, 7);
diff --git a/pkg/linter/lib/src/rules/missing_whitespace_between_adjacent_strings.dart b/pkg/linter/lib/src/rules/missing_whitespace_between_adjacent_strings.dart
index 4dcd451..3f48868 100644
--- a/pkg/linter/lib/src/rules/missing_whitespace_between_adjacent_strings.dart
+++ b/pkg/linter/lib/src/rules/missing_whitespace_between_adjacent_strings.dart
@@ -67,7 +67,7 @@
   static bool _isRegExpInstanceCreation(AstNode? node) {
     if (node is InstanceCreationExpression) {
       var constructorElement = node.constructorName.element;
-      return constructorElement?.enclosingElement2?.name == 'RegExp';
+      return constructorElement?.enclosingElement2.name == 'RegExp';
     }
     return false;
   }
diff --git a/pkg/linter/lib/src/rules/valid_regexps.dart b/pkg/linter/lib/src/rules/valid_regexps.dart
index cc216bb..7cd9250 100644
--- a/pkg/linter/lib/src/rules/valid_regexps.dart
+++ b/pkg/linter/lib/src/rules/valid_regexps.dart
@@ -38,7 +38,7 @@
     var element = node.constructorName.element?.enclosingElement2;
     if (element == null) return;
 
-    if (element.name == 'RegExp' && (element.library2?.isDartCore ?? false)) {
+    if (element.name == 'RegExp' && element.library2.isDartCore) {
       var args = node.argumentList.arguments;
       if (args.isEmpty) return;
 
diff --git a/pkg/vm_service/test/wildcard_test.dart b/pkg/vm_service/test/wildcard_test.dart
index 7d1ebe5..3d87728 100644
--- a/pkg/vm_service/test/wildcard_test.dart
+++ b/pkg/vm_service/test/wildcard_test.dart
@@ -5,7 +5,7 @@
 // SharedOptions=--enable-experiment=wildcard-variables
 
 // ignore: invalid_language_version_override
-// @dart = 3.6
+// @dart = 3.7
 
 import 'dart:developer';
 // Tests wildcard import prefixes.
diff --git a/runtime/bin/BUILD.gn b/runtime/bin/BUILD.gn
index b2582ca..c06e619 100644
--- a/runtime/bin/BUILD.gn
+++ b/runtime/bin/BUILD.gn
@@ -10,6 +10,7 @@
 import("../vm/compiler/compiler_sources.gni")
 import("../vm/ffi/ffi_sources.gni")
 import("../vm/heap/heap_sources.gni")
+import("../vm/regexp/regexp_sources.gni")
 import("../vm/vm_sources.gni")
 import("builtin_impl_sources.gni")
 import("builtin_sources.gni")
@@ -1055,6 +1056,7 @@
   vm_tests = rebase_path(vm_sources_tests, ".", "../vm")
   compiler_tests = rebase_path(compiler_sources_tests, ".", "../vm/compiler")
   heap_tests = rebase_path(heap_sources_tests, ".", "../vm/heap")
+  regexp_tests = rebase_path(regexp_sources_tests, ".", "../vm/regexp")
 
   sources = [
               "builtin.cc",
@@ -1072,7 +1074,7 @@
               "vmservice_impl.cc",
               "vmservice_impl.h",
             ] + builtin_impl_tests + vm_tests + compiler_tests + heap_tests +
-            io_impl_tests
+            regexp_tests + io_impl_tests
 }
 
 executable("run_vm_tests") {
diff --git a/runtime/lib/regexp.cc b/runtime/lib/regexp.cc
index 3fd92bf..1852c51 100644
--- a/runtime/lib/regexp.cc
+++ b/runtime/lib/regexp.cc
@@ -9,14 +9,14 @@
 #include "vm/native_entry.h"
 #include "vm/object.h"
 #include "vm/object_store.h"
-#include "vm/regexp_assembler_bytecode.h"
-#include "vm/regexp_parser.h"
+#include "vm/regexp/regexp_assembler_bytecode.h"
+#include "vm/regexp/regexp_parser.h"
 #include "vm/reusable_handles.h"
 #include "vm/symbols.h"
 #include "vm/thread.h"
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
-#include "vm/regexp_assembler_ir.h"
+#include "vm/regexp/regexp_assembler_ir.h"
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
 namespace dart {
diff --git a/runtime/tools/profiling/.gitignore b/runtime/tools/profiling/.gitignore
new file mode 100644
index 0000000..3a85790
--- /dev/null
+++ b/runtime/tools/profiling/.gitignore
@@ -0,0 +1,3 @@
+# https://dart.dev/guides/libraries/private-files
+# Created by `dart pub`
+.dart_tool/
diff --git a/runtime/tools/profiling/CHANGELOG.md b/runtime/tools/profiling/CHANGELOG.md
new file mode 100644
index 0000000..a0712a7
--- /dev/null
+++ b/runtime/tools/profiling/CHANGELOG.md
@@ -0,0 +1,3 @@
+## 0.1.0
+
+- Initial version.
diff --git a/runtime/tools/profiling/README.md b/runtime/tools/profiling/README.md
new file mode 100644
index 0000000..53a31e6
--- /dev/null
+++ b/runtime/tools/profiling/README.md
@@ -0,0 +1,64 @@
+Various tools for low level profing of code running on the Dart VM.
+
+# Uprobe based profiling
+
+[uprobes](https://www.kernel.org/doc/html/latest/trace/uprobetracer.html) is
+a user-space dynamic tracing mechanism. Using this mechanism the kernel can
+be instructed to place a tracepoint at a particular file offset within a
+specific binary. Whenever this tracepoint is hit the kernel will fetch values
+from the execution context based on the uprobe's description and emit an event.
+A developer can subscribe to uprobe events in a few different ways including
+[perf_event_open](https://man7.org/linux/man-pages/man2/perf_event_open.2.html)
+syscall. uprobes have been enabled by default on all newish Linux kernels
+(4.14+), however they are only truly usable on Android/ARM64 starting from
+5.10+. `bin/set_uprobe.dart` is a helper script for placing uprobes inside
+binaries and using this for profiling.
+
+The core workflow looks like this:
+
+```console
+$ sudo $(which dart) runtime/tools/profiling/bin/set_uprobe.dart probeName symbol binary
+```
+
+This will create an uprobe with name `probeName` which triggers whenever
+the given `symbol` inside the given `binary` is called. You can then record
+an event (and collect the call stack) using:
+
+```console
+$ sudo perf record -g -e uprobes:probeName ...
+```
+
+## Allocation profiling with uprobes
+
+AOT compiler can emit a special probe point (`stub AllocationProbePoint`) which
+triggers for each new space allocation from generated code. `set_uprobe` script
+has special support for this probe point: it will configure probe point to
+record additional information (address of allocated object, allocation top and
+cid of the allocated object) allowing to post process collected data into
+an actual allocation profile.
+
+Start by compiling your application with `--generate-probe-points`:
+
+```console
+$ pkg/vm/tool/precompiler2 --generate-probe-points test.dart test.aot
+```
+
+Then install uprobe on `AllocationProbePoint`:
+
+```console
+$ sudo $(which dart) runtime/tools/profiling/bin/set_uprobe.dart alloc AllocationProbePoint test.aot
+```
+
+Record the profile:
+
+```
+$ sudo perf record -g -e uprobes:alloc out/ReleaseX64/dart_precompiled_runtime test.aot
+$ sudo chmod 0755 perf.data
+```
+
+Produce a coalesced allocation profile from the recording:
+
+```
+$ dart runtime/tools/profiling/bin/convert_allocation_profile.dart perf.data
+$ pprof -flame pprof.profile
+```
\ No newline at end of file
diff --git a/runtime/tools/profiling/analysis_options.yaml b/runtime/tools/profiling/analysis_options.yaml
new file mode 100644
index 0000000..dee8927
--- /dev/null
+++ b/runtime/tools/profiling/analysis_options.yaml
@@ -0,0 +1,30 @@
+# This file configures the static analysis results for your project (errors,
+# warnings, and lints).
+#
+# This enables the 'recommended' set of lints from `package:lints`.
+# This set helps identify many issues that may lead to problems when running
+# or consuming Dart code, and enforces writing Dart using a single, idiomatic
+# style and format.
+#
+# If you want a smaller set of lints you can change this to specify
+# 'package:lints/core.yaml'. These are just the most critical lints
+# (the recommended set includes the core lints).
+# The core lints are also what is used by pub.dev for scoring packages.
+
+include: package:lints/recommended.yaml
+
+# Uncomment the following section to specify additional rules.
+
+# linter:
+#   rules:
+#     - camel_case_types
+
+# analyzer:
+#   exclude:
+#     - path/to/excluded/files/**
+
+# For more information about the core and recommended set of lints, see
+# https://dart.dev/go/core-lints
+
+# For additional information about configuring this file, see
+# https://dart.dev/guides/language/analysis-options
diff --git a/runtime/tools/profiling/bin/convert_allocation_profile.dart b/runtime/tools/profiling/bin/convert_allocation_profile.dart
new file mode 100644
index 0000000..11ef149
--- /dev/null
+++ b/runtime/tools/profiling/bin/convert_allocation_profile.dart
@@ -0,0 +1,537 @@
+// Copyright (c) 2024, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:io';
+import 'dart:typed_data';
+import 'dart:ffi';
+
+import 'package:fixnum/fixnum.dart' hide Int32;
+
+import 'package:profiling/src/perf/perf_data.dart';
+import 'package:profiling/src/symbols.dart';
+import 'package:profiling/src/pprof/generated/profile.pb.dart' as pprof;
+
+/// `PERF_RECORD_SAMPLE` with the following optional fields:
+///
+/// `PERF_SAMPLE_IP`, `PERF_SAMPLE_TID`, `PERF_SAMPLE_TIME`, `PERF_SAMPLE_CALLCHAIN`,
+/// `PERF_SAMPLE_CPU`, `PERF_SAMPLE_PERIOD`, `PERF_SAMPLE_RAW`.
+///
+/// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/include/uapi/linux/perf_event.h#L947
+final class SampleEvent extends Struct {
+  external EventHeader header;
+
+  /// Enabled by `PERF_SAMPLE_IP`
+  @Uint64()
+  external int ip;
+
+  /// Enabled by `PERF_SAMPLE_TID`
+  @Uint32()
+  external int pid;
+
+  /// Enabled by `PERF_SAMPLE_TID`
+  @Uint32()
+  external int tid;
+
+  /// Enabled by `PERF_SAMPLE_TIME`
+  @Uint64()
+  external int time;
+
+  /// Enabled by `PERF_SAMPLE_CPU`
+  @Uint32()
+  external int cpu;
+
+  /// Enabled by `PERF_SAMPLE_CPU`
+  @Uint32()
+  external int res;
+
+  /// Enabled by `PERF_SAMPLE_PERIOD`
+  @Uint64()
+  external int period;
+
+  /// Enabled by `PERF_SAMPLE_CALLCHAIN`
+  @Uint64()
+  external int nr;
+
+  /// Enabled by `PERF_SAMPLE_CALLCHAIN`
+  @Array.variable()
+  external Array<Uint64> ips;
+}
+
+/// Data recorded by the probe stored in a `PERF_SAMPLE_RAW`.
+///
+/// The `size` field is part of `PERF_SAMPLE_RAW` encoding the rest are
+/// part of probe data itself. Format for the recorded data can be recovered
+/// by loading tracepoint information from an optional section identified
+/// by `HEADER_TRACING_DATA` ([OptionalSection.tracingData]). However encoding
+/// of that section is extremely bespoke (see `trace-event-read.c` below), so
+/// instead of fiddling with that we simply hardcode expected format of the
+/// probe. This obviously needs to be kept in sync with `set_uprobe.dart`
+/// script.
+///
+/// ```
+/// $ sudo cat /sys/kernel/tracing/events/uprobes/alloc/format
+/// name: alloc
+/// ID: 1976
+/// format:
+///         field:unsigned short common_type;       offset:0;       size:2; signed:0;
+///         field:unsigned char common_flags;       offset:2;       size:1; signed:0;
+///         field:unsigned char common_preempt_count;       offset:3;       size:1; signed:0;
+///         field:int common_pid;   offset:4;       size:4; signed:1;
+///
+///         field:unsigned long __probe_ip; offset:8;       size:8; signed:0;
+///         field:s64 addr; offset:16;      size:8; signed:1;
+///         field:s64 top;  offset:24;      size:8; signed:1;
+///         field:u32 cid;  offset:32;      size:4; signed:0;
+/// print fmt: "(%lx) addr=%Ld top=%Ld cid=%u", REC->__probe_ip, REC->addr, REC->top, REC->cid
+/// ```
+///
+/// [^1]: https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/tools/perf/util/trace-event-read.c#L375
+@Packed(1)
+final class ProbeData extends Struct {
+  @Uint32()
+  external int size;
+
+  @Uint16()
+  external int commonType;
+
+  @Uint8()
+  external int commonFlags;
+
+  @Uint8()
+  external int commonPreemptCount;
+
+  @Int32()
+  external int commonPid;
+
+  @Uint64()
+  external int probeIp;
+
+  @Uint64()
+  external int addr;
+
+  @Uint64()
+  external int top;
+
+  @Uint32()
+  external int cid;
+
+  @override
+  String toString() =>
+      'Probe{addr=${addr.formatAsAddress()},top=${top.formatAsAddress()},cid=$cid}';
+}
+
+/// Lazily populated mapping between file offsets in a binary and profile
+/// location ids.
+///
+/// This class handles convertion of the file offset to the corresponding
+/// symbol name and futher into corresponding location id inside the profile.
+final class SymbolsIndex {
+  final ProfileBuilder profileBuilder;
+
+  final Symbols symbols;
+  final List<Int64?> ids;
+
+  SymbolsIndex(this.profileBuilder, this.symbols)
+      : ids = List<Int64?>.filled(symbols.fileOffsets.length, null);
+
+  static final lineRe =
+      RegExp(r"^(?<addr>[0-9a-f]+)\s+(?<typ>\w+)\s+(?<name>.*)$");
+
+  /// Return location id corresponding to the given [fileOffset].
+  ///
+  /// This function will lazily allocate new ids as necessary by
+  /// calling [ProfileBuilder.addSymbol].
+  Int64? symbolId(int fileOffset) {
+    final index = symbols.symbolIndex(fileOffset);
+    if (index != null) {
+      return (ids[index] ??= profileBuilder.addSymbol(symbols.names[index]));
+    }
+    return null;
+  }
+}
+
+final class Mapping {
+  final int baseAddress;
+  final int length;
+  final String path;
+  final int offset;
+
+  Mapping({
+    required this.baseAddress,
+    required this.length,
+    required this.path,
+    required this.offset,
+  });
+}
+
+/// Symbols information for the whole address space.
+final class AddressSpaceSymbols {
+  /// Base addresses for mapping ranges.
+  ///
+  /// To simplify search we also add ranges that don't have any symbols here.
+  /// Consider for example that we have two mappings `[A, A')` and `[B, B')`
+  /// with symbols (`Sym(A)` and `Sym(B)` respectively). In this case:
+  ///   * [baseAddresses] will contain `[0, A, A', B, B']` and
+  ///   * [symbolsIndexes] will contain `[null, SA, null, SymB, null]`.
+  final Int64List baseAddresses;
+
+  /// Symbol indexes corresponding to mappings in [baseAddresses].
+  final List<SymbolsIndex?> symbolsIndexes;
+
+  /// File offsets corresponding to mappings in [baseAddresses].
+  final Int64List fileOffsets;
+
+  AddressSpaceSymbols._(
+      this.baseAddresses, this.symbolsIndexes, this.fileOffsets);
+
+  Int64? symbolId(int address) {
+    // We use linear search because we assume the number of mappings
+    // is very small (~2).
+    final limit = baseAddresses.length - 1;
+    for (var i = 0; i < limit; i++) {
+      final start = baseAddresses[i];
+      final end = baseAddresses[i + 1];
+      if (start <= address && address < end) {
+        final fileOffset = address - start + fileOffsets[i];
+        return symbolsIndexes[i]?.symbolId(fileOffset);
+      }
+    }
+    return null;
+  }
+
+  /// Construct [AddressSpaceSymbols] from [Mapping] records loaded from
+  /// `perf.data`.
+  static AddressSpaceSymbols fromMappings(
+      List<Mapping> mappings, ProfileBuilder profileBuilder) {
+    // Try loading symbols for each mapping and keep those that
+    // actually have symbols. Sort resulting list by base address.
+    final mappingsWithSymbols = <(Mapping, SymbolsIndex)>[];
+    for (var event in mappings) {
+      final symbolsIndex = profileBuilder.symbolsIndexFor(event.path);
+      if (symbolsIndex != null) {
+        mappingsWithSymbols.add((event, symbolsIndex));
+      }
+    }
+    mappingsWithSymbols
+        .sort((a, b) => a.$1.baseAddress.compareTo(b.$1.baseAddress));
+
+    // Build `AddressSpaceSymbols` from mappings with symbols.
+    //
+    // Note: we need to accomodate for a situation when two mappings are
+    // adjacent. However we assume that number of mappings is rather small
+    // so we don't optimize this code too much.
+    final result = <({int baseAddress, SymbolsIndex? index, int fileOffset})>[];
+    void addEntry({
+      required int baseAddress,
+      required SymbolsIndex? index,
+      required int fileOffset,
+    }) {
+      if (result.isNotEmpty && result.last.baseAddress == baseAddress) {
+        // Collapse end of the previous mapping and the start of the new
+        // mapping.
+        if (result.last.index != null) {
+          throw StateError('Unexpected intersection of address ranges');
+        }
+        result.removeLast();
+      }
+      result.add(
+          (baseAddress: baseAddress, index: index, fileOffset: fileOffset));
+    }
+
+    addEntry(baseAddress: 0, index: null, fileOffset: 0);
+    for (var e in mappingsWithSymbols) {
+      addEntry(
+        baseAddress: e.$1.baseAddress,
+        index: e.$2,
+        fileOffset: e.$1.offset,
+      );
+      addEntry(
+        baseAddress: e.$1.baseAddress + e.$1.length,
+        index: null,
+        fileOffset: 0,
+      );
+    }
+
+    // Split result into individual components.
+    return AddressSpaceSymbols._(
+      Int64List.fromList(
+        result.map((e) => e.baseAddress).toList(growable: false),
+      ),
+      result.map((e) => e.index).toList(growable: false),
+      Int64List.fromList(
+        result.map((e) => e.fileOffset).toList(growable: false),
+      ),
+    );
+  }
+}
+
+/// A trie node representing a callstack frame.
+///
+/// To minimize the size of the produced `pprof.profile` we collapse all
+/// matching callstacks into a single `pprof.Sample` entry in the profile.
+/// This is done by through a simple [trie][1] data structure.
+///
+/// Nodes corresponding to callstacks from original profile will have not-null
+/// non-zero [totalBytes] associated with them. Path to these nodes should
+/// be flushed into [pprof.Profile] as individual [pprof.Sample] entries
+/// at the end of conversion. See [flushTo].
+///
+/// [1]: https://en.wikipedia.org/wiki/Trie
+final class CallStackTrieNode {
+  /// [pprof.Profile] location id corresponding to this frame.
+  final Int64 id;
+
+  /// Total number of bytes allocated by this frame.
+  int totalBytes = 0;
+
+  /// Callees of this frame.
+  late final Map<Int64, CallStackTrieNode> children =
+      <Int64, CallStackTrieNode>{};
+
+  CallStackTrieNode({required this.id});
+
+  CallStackTrieNode operator [](Int64 id) =>
+      children[id] ??= CallStackTrieNode(id: id);
+
+  void flushTo(pprof.Profile profile, List<Int64> path) {
+    if (totalBytes != 0) {
+      profile.sample.add(
+        pprof.Sample(locationId: path.reversed)..value.add(Int64(totalBytes)),
+      );
+    }
+    for (var child in children.values) {
+      path.add(child.id);
+      child.flushTo(profile, path);
+      path.removeLast();
+    }
+  }
+}
+
+/// Helper for building [pprof.Profile].
+///
+/// It takes care of indexing symbols and managing their ids.
+final class ProfileBuilder {
+  final profile = pprof.Profile();
+
+  final symbolTable = <String, Int64>{};
+  final locationIds = <String, Int64>{};
+
+  final callStackTrieRoot = CallStackTrieNode(id: Int64(-1));
+
+  ProfileBuilder() {
+    addString("");
+    profile.sampleType.add(pprof.ValueType(
+      type: addString('space'),
+      unit: addString('bytes'),
+    ));
+  }
+
+  Int64 addString(String str) {
+    var id = symbolTable[str];
+    if (id != null) {
+      return id;
+    }
+    id = symbolTable[str] = Int64(symbolTable.length);
+    profile.stringTable.add(str);
+    return id;
+  }
+
+  Int64 addSymbol(String symbol) {
+    var id = locationIds[symbol];
+    if (id != null) {
+      return id;
+    }
+    id = locationIds[symbol] = Int64(locationIds.length + 1);
+    profile.function.add(pprof.Function_(id: id, name: addString(symbol)));
+    profile.location
+        .add(pprof.Location(id: id, line: [pprof.Line(functionId: id)]));
+    return id;
+  }
+
+  SymbolsIndex? symbolsIndexFor(String path) {
+    final symbols = Symbols.load(path);
+    if (symbols == null) {
+      return null;
+    }
+    return SymbolsIndex(this, symbols);
+  }
+
+  pprof.Profile finishProfile() {
+    callStackTrieRoot.flushTo(profile, []);
+    return profile;
+  }
+}
+
+/// Helper for converting raw callstack into its symbolized form.
+///
+/// We assume that callstack for each new sample usually shares its prefix
+/// (e.g. outermost callers, like `main`) with the previously processed
+/// sample. This allows us to reuse location ids from the previous sample
+/// for the large portion of the stack.
+final class SymbolizedCallStackBuilder {
+  /// Depth of the current stack.
+  int depth = 0;
+
+  /// Raw addresses for each frame in the caller to callee order.
+  ///
+  /// We do not clear this array between samples allowing us to detect
+  /// situations when we can reuse entries. Only entries `0..depth-1`
+  /// correspond to the current stack. Other entries originate from
+  /// previous samples and might be out of sync with the current sample.
+  ///
+  /// (`0` is the outermost caller, `1` is its callee, etc).
+  final pcs = Int64List(200);
+
+  /// Trie nodes corresponding to each frame in the stack.
+  ///
+  /// For entries in the `0..depth-2` range `nodes[i]` is parent of
+  /// `nodes[i+1]` .
+  ///
+  final nodes = List<CallStackTrieNode?>.filled(200, null);
+
+  /// Trie node for the last frame (either `nodes[depth-1]` or root trie node
+  /// if [depth] is `0`).
+  CallStackTrieNode last;
+
+  /// `true` when the callstack which is currently being built matches
+  /// the prefix of the previous callstack.
+  bool prefixMatches = true;
+
+  SymbolizedCallStackBuilder(ProfileBuilder builder)
+      : last = builder.callStackTrieRoot;
+
+  void add(int pc, AddressSpaceSymbols syms) {
+    if (pcs[depth] != pc) {
+      // Mismatch between newly added `pc` and the `pc` we have from the
+      // previous sample. We need to lookup location id for it.
+      final id = syms.symbolId(pc);
+      if (id == null) {
+        // No symbol - drop the frame.
+        return;
+      }
+
+      pcs[depth] = pc;
+      last = last[id];
+
+      // We might still hit the same node in the trie.
+      if (nodes[depth] != last) {
+        nodes[depth] = last;
+        // From here onward we can't reuse `nodes` because the path has
+        // diverged.
+        prefixMatches = false;
+      }
+    } else if (!prefixMatches) {
+      // Address might match - but the path we got here might be different. This
+      // means we can reuse `id` from the node, but not the node itself.
+      final id = nodes[depth]!.id;
+      nodes[depth] = last = last[id];
+    } else {
+      // This pc *and* the all previous nodes match. We can just reuse
+      // the node.
+      last = nodes[depth]!;
+    }
+    depth++;
+  }
+
+  void addTo(ProfileBuilder builder, int allocatedBytes) {
+    last.totalBytes += allocatedBytes;
+  }
+
+  void reset(ProfileBuilder builder) {
+    depth = 0;
+    last = builder.callStackTrieRoot;
+    prefixMatches = true;
+  }
+}
+
+@pragma('vm:never-inline')
+pprof.Profile buildProfileFromPerfData(String path) {
+  final raf = File(path).openSync();
+
+  final profileBuilder = ProfileBuilder();
+  final perfData = PerfData(raf);
+
+  // Check that input file has expected format.
+  final allAttrs = perfData.readAttrs();
+  if (allAttrs.length != 1) {
+    perfData.reportError(
+        'Expected single perf_event_attrs structure, got ${allAttrs.length}');
+  }
+
+  final attrs = allAttrs.first;
+
+  if (attrs.type != TypeId.tracepoint) {
+    perfData.reportError('Expected to find a file with tracepoint events');
+  }
+
+  const expectedSampleFormat = SampleFormat.ip |
+      SampleFormat.tid |
+      SampleFormat.time |
+      SampleFormat.callchain |
+      SampleFormat.cpu |
+      SampleFormat.period |
+      SampleFormat.raw;
+  if (attrs.sampleType != expectedSampleFormat) {
+    perfData.reportError(
+        'Expected to sample format ${SampleFormat.format(expectedSampleFormat)}'
+        ' got ${SampleFormat.format(attrs.sampleType)}: difference '
+        '${SampleFormat.format(attrs.sampleType ^ expectedSampleFormat)}');
+  }
+
+  final mappings = <Mapping>[];
+  perfData.readEvents((type, chunk, pos) {
+    if (type == EventType.mmap2) {
+      final event = Struct.create<Mmap2Event>(chunk, pos);
+      mappings.add(Mapping(
+        baseAddress: event.addr,
+        length: event.len,
+        path: event.filename.toStringFromZeroTerminated(),
+        offset: event.pgoffs,
+      ));
+    } else if (type == EventType.sample && mappings.isNotEmpty) {
+      // TODO: we miss one sample here.
+      return false; // Break iteration.
+    }
+    return true;
+  });
+
+  final syms = AddressSpaceSymbols.fromMappings(mappings, profileBuilder);
+  final stack = SymbolizedCallStackBuilder(profileBuilder);
+  perfData.readEvents((type, chunk, pos) {
+    if (type == EventType.sample) {
+      final sample = Struct.create<SampleEvent>(chunk, pos);
+      final probeData = Struct.create<ProbeData>(
+          chunk, pos + sizeOf<SampleEvent>() + sample.nr * 8);
+
+      for (var i = sample.nr - 1; i > 1; i--) {
+        stack.add(sample.ips[i], syms);
+      }
+      if (stack.depth > 0) {
+        // Accumulate [totalBytes] in the last node.
+        stack.last.totalBytes += probeData.top - probeData.addr - 1;
+      }
+
+      // Reset the stack for the next sample.
+      stack.reset(profileBuilder);
+    }
+
+    return true;
+  });
+
+  print('All data loaded - creating profile.');
+  return profileBuilder.finishProfile();
+}
+
+void main(List<String> args) async {
+  final perfDataPath = args[0];
+  print('loading $perfDataPath');
+  final profile = buildProfileFromPerfData(perfDataPath);
+  print('created ${profile.sample.length} samples');
+
+  print('Serializing proto (pprof.profile)');
+  final result = profile.writeToBuffer();
+  print('... ${result.length} bytes');
+  File('pprof.profile').writeAsBytesSync(result);
+  print('Done');
+}
diff --git a/runtime/tools/profiling/bin/set_uprobe.dart b/runtime/tools/profiling/bin/set_uprobe.dart
new file mode 100644
index 0000000..811767e
--- /dev/null
+++ b/runtime/tools/profiling/bin/set_uprobe.dart
@@ -0,0 +1,135 @@
+// Copyright (c) 2024, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:io';
+
+import 'package:path/path.dart' as p;
+
+import 'package:profiling/src/elf_utils.dart';
+
+// TODO(vegorov): update this to support Android ARM64 both for standalone
+// binaries and Flutter applications. Prototype code for that is available
+// in https://dart-review.googlesource.com/c/sdk/+/239661.
+void main(List<String> args) async {
+  if (args.length != 3) {
+    print(
+        'Usage: pkg/vm/tool/set_uprobe.dart <probe-name> <symbol> <AOT snapshot SO file>');
+    exit(-1);
+  }
+
+  final [probeName, symbol, sharedObject] = args;
+
+  final uprobeAddress =
+      await _computeProbesVirtualAddress(sharedObject, symbol);
+  final loadingBias = loadingBiasOf(sharedObject);
+
+  final uprobeFileOffset = (uprobeAddress + loadingBias).toRadixString(16);
+
+  final soName = p.basename(sharedObject);
+  final soPath = p.canonicalize(p.absolute(sharedObject));
+
+  // TODO(vegorov) ARM64 support
+  final threadRegister = "r14";
+  final resultRegister = "ax";
+
+  final uprobeFormat = symbol == 'AllocationProbePoint'
+      ? 'addr=%$resultRegister:s64 top=+${await _getThreadTopOffset()}(%$threadRegister):s64 cid=-1(%$resultRegister):b20@12/32'
+      : '';
+
+  final probe = 'p:$probeName $soPath:0x$uprobeFileOffset $uprobeFormat';
+  print(probe);
+
+  File('/sys/kernel/tracing/uprobe_events').writeAsStringSync(probe);
+}
+
+Future<int> _computeProbesVirtualAddress(
+    String sharedObject, String targetSymbol) async {
+  int offset = 0;
+  if (targetSymbol == 'AllocationProbePoint') {
+    offset = await _determineAllocProbeOffset(sharedObject);
+  }
+
+  final targetRe = RegExp('\\b$targetSymbol\\b');
+  final matches = <String, int>{
+    for (final (:addr, :name) in textSymbolsOf(sharedObject))
+      if (targetRe.hasMatch(name)) name: addr,
+  };
+
+  if (matches.isEmpty) {
+    throw 'Symbol $targetSymbol not found in $sharedObject';
+  }
+
+  if (matches.length != 1) {
+    throw 'Multiple symbols match: ${matches.keys}';
+  }
+
+  final entry = matches.entries.single;
+  print('placing uprobe on ${entry.key} at '
+      '0x${entry.value.toRadixString(16)}+$offset');
+  return entry.value + offset;
+}
+
+// `AllocationProbePoint` stub should have a probe placed at a place where
+// stack frame is properly setup so that unwinding succeeds. The stub itself
+// contains a dummy test immediate instruction which encodes the offset at
+// which the probe should be placed.
+Future<int> _determineAllocProbeOffset(String sharedObject) async {
+  // Dump SO file to get the address of the interesting symbol.
+  final disassembly = await _exec('llvm-objdump', [
+    '--disassemble-symbols=stub AllocationProbePoint',
+    '-Mintel',
+    sharedObject,
+  ]);
+
+  // We are looking for `test al, imm` or `tst x0, #imm` where `imm` is a
+  // hexadecimal immediate encoding offset to the probe point within the stub.
+  final pattern = RegExp(
+      r'^\s+[a-f0-9]+:(( [a-f0-9]{2})+| [a-f0-9]{8})\s+(test|tst)\s+(al|x0),\s+#?0x(?<offset>[0-9a-f]+)\s*$',
+      multiLine: true);
+
+  final match = pattern.firstMatch(disassembly);
+  if (match == null) {
+    print(disassembly);
+    throw StateError(
+        'failed to find test-immediate instruction encoding the probe offset');
+  }
+
+  return int.parse(match.namedGroup('offset')!, radix: 16);
+}
+
+Future<String> _getThreadTopOffset() async {
+  // TODO(vegorov) ARM64 support
+  final sdkSrc = Platform.script.resolve('../../../..').toFilePath();
+  await _exec(
+      'ninja', ['-C', 'out/ReleaseX64', '-j1000', '-l64', 'offsets_extractor'],
+      workingDirectory: sdkSrc);
+  final offsets =
+      await _exec(p.join(sdkSrc, 'out/ReleaseX64/offsets_extractor'), []);
+  final line = offsets
+      .split('\n')
+      .firstWhere((line) => line.contains('Thread_top_offset'));
+  final offset = RegExp(r' = (?<offset>0x[a-f\d]+);$')
+      .firstMatch(line)!
+      .namedGroup('offset')!;
+
+  return int.parse(offset).toString();
+}
+
+Future<String> _exec(String executable, List<String> args,
+    {String? workingDirectory}) async {
+  final result =
+      await Process.run(executable, args, workingDirectory: workingDirectory);
+  if (result.exitCode != 0) {
+    throw StateError('''
+Failed to run $executable ${args.join(' ')}
+stdout:
+${result.stdout}
+
+stderr:
+
+${result.stderr}
+''');
+  }
+  return result.stdout as String;
+}
diff --git a/runtime/tools/profiling/lib/src/elf_utils.dart b/runtime/tools/profiling/lib/src/elf_utils.dart
new file mode 100644
index 0000000..5ba8573
--- /dev/null
+++ b/runtime/tools/profiling/lib/src/elf_utils.dart
@@ -0,0 +1,44 @@
+// Copyright (c) 2024, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:io';
+
+/// Compute the difference between virtual address and the file offset of the
+/// TEXT section. It can be used to convert virtual addresses into
+/// file offsets.
+int loadingBiasOf(String path) {
+  final data = Process.runSync('llvm-readelf', ['-l', path]).stdout.split("\n");
+  for (var line in data) {
+    line = line.trim();
+    if (line.startsWith("LOAD") && line.contains("R E")) {
+      final components = line.split(RegExp(r"\s+"));
+      final fileOffset = int.parse(components[1]);
+      final virtAddr = int.parse(components[2]);
+      return virtAddr - fileOffset;
+    }
+  }
+  throw StateError('Unable to determine loading bias for $path');
+}
+
+/// Iterate over all symbols in TEXT section of the given binary.
+Iterable<({int addr, String name})> textSymbolsOf(String path) {
+  // Run `nm -C` on a binary to extract demangled (-C) symbols.
+  final output = Process.runSync('/usr/bin/nm', ['-C', path]);
+  final result = (output.stdout as String).split('\n');
+  if (output.exitCode != 0) throw 'failed to run nm';
+
+  // Parse `nm` output looking for `t` (TEXT) symbols. Each line
+  // has the following format:
+  final lineRe = RegExp(r"^(?<addr>[0-9a-f]+)\s+(?<typ>\w+)\s+(?<name>.*)$");
+  // final symbols = <(int, String)>[];
+  return result.map((line) {
+    final m = lineRe.firstMatch(line);
+    if (m != null && m.namedGroup('typ') == 't') {
+      final addr = int.parse(m.namedGroup('addr')!, radix: 16);
+      final name = m.namedGroup('name')!;
+      return (addr: addr, name: name);
+    }
+    return null;
+  }).nonNulls;
+}
diff --git a/runtime/tools/profiling/lib/src/perf/perf_data.dart b/runtime/tools/profiling/lib/src/perf/perf_data.dart
new file mode 100644
index 0000000..c5c0c5c
--- /dev/null
+++ b/runtime/tools/profiling/lib/src/perf/perf_data.dart
@@ -0,0 +1,630 @@
+// Copyright (c) 2024, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/// This library file contains data structures and helper methods for parsing
+/// `perf.data` files produced by `perf` tool on Linux.
+///
+/// Format of this file is documented in:
+///
+///   * https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/tools/perf/Documentation/perf.data-file-format.txt
+///   * https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/tools/perf/util/header.h
+///   * https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/include/uapi/linux/perf_event.h
+///
+library;
+
+import 'dart:ffi';
+import 'dart:io';
+import 'dart:math' as math;
+import 'dart:typed_data';
+
+/// `struct perf_header`: header of the `perf.data` file.
+///
+/// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/tools/perf/util/header.h#L64
+final class Header extends Struct {
+  @Array(8)
+  external Array<Uint8> magic;
+
+  @Uint64()
+  external int size;
+
+  @Uint64()
+  external int attrSize;
+
+  external FileSection attrs;
+  external FileSection data;
+  external FileSection eventTypes;
+
+  @Uint64()
+  external int flags;
+
+  @Array(3)
+  external Array<Uint64> flags1;
+}
+
+/// `struct perf_file_section`: section inside `perf.data` file.
+///
+/// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/tools/perf/util/header.h#L59
+final class FileSection extends Struct {
+  @Uint64()
+  external int offset;
+
+  @Uint64()
+  external int size;
+
+  @override
+  String toString() {
+    return 'PerfFileSection{offset=$offset,size=$size}';
+  }
+}
+
+/// Optional sections inside `perf.data` file.
+///
+/// The section is present iff corresponding bit in [PerfHeader.flags] is set.
+///
+/// `PerfFileSection` descriptors for present sections will follow in sequence
+/// immediately after the data section (i.e. the first `PerfFileSection` will
+/// be located at `header.data.offset + header.data.size` offset).
+///
+/// See [PerfData.readOptionalSectionHeaders].
+///
+/// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/tools/perf/util/header.h#L15
+enum OptionalSection {
+  reserved,
+  tracingData,
+  buildId,
+  hostname,
+  osRelease,
+  version,
+  arch,
+  nrCpus,
+  cpuDesc,
+  cpuId,
+  totalMem,
+  cmdLine,
+  eventDesc,
+  cpuTopology,
+  numaTopology,
+  branchStack,
+  groupDesc,
+  auxTrace,
+  stat,
+  cache,
+  sampleTime,
+  sampleTopology,
+  clockId,
+  dirFormat,
+  bpfProgInfo,
+  bpfBtf,
+  compressed,
+  cpuPmuCaps,
+  clockData,
+  hybridTopology,
+  pmuCaps
+}
+
+/// `perf_event_header`: common header of all event entries.
+///
+/// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/include/uapi/linux/perf_event.h#L815
+final class EventHeader extends Struct {
+  @Uint32()
+  external int type;
+
+  @Uint16()
+  external int misc;
+
+  @Uint16()
+  external int size;
+
+  @override
+  String toString() => 'PerfEventHeader{type=$type,misc=$misc,size=$size}';
+}
+
+/// `perf_event_attr`: configuration of the event monitored by `perf`.
+///
+/// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/include/uapi/linux/perf_event.h#L389
+final class EventAttr extends Struct {
+  /// Major type: hardware/software/tracepoint/etc.
+  ///
+  /// See [EventType].
+  @Uint32()
+  external int type;
+
+  @Uint32()
+  external int size;
+
+  /// Type specific configuration information.
+  @Uint64()
+  external int config;
+
+  @Uint64()
+  external int samplePeriodOrFreq;
+
+  @Uint64()
+  external int sampleType;
+
+  @Uint64()
+  external int readFormat;
+
+  /// Various bit fields which we currently don't care about.
+  ///
+  /// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/include/uapi/linux/perf_event.h#L414
+  @Uint64()
+  external int flags;
+
+  @Uint32()
+  external int wakeupEventOrWatermark;
+  @Uint32()
+  external int bpType;
+
+  /// Union of `bp_addr`/`kprobe_func`/`uprobe_path`/`config1`
+  @Uint64()
+  external int config1;
+
+  /// Union of `bp_len`/`kprobe_addr`/`probe_offset`/`config2`
+  @Uint64()
+  external int config2;
+
+  /// One of `enum perf_branch_sample_type`
+  @Uint64()
+  external int branchSampleType;
+
+  /// Defines set of user regs to dump on samples.
+  /// See asm/perf_regs.h for details.
+  @Uint64()
+  external int sampleRegsUser;
+
+  /// Defines size of the user stack to dump on samples.
+  @Uint32()
+  external int sampleStackUser;
+
+  @Int32()
+  external int clockid;
+
+  /// Defines set of regs to dump for each sample
+  /// state captured on:
+  ///  - precise = 0: PMU interrupt
+  ///  - precise > 0: sampled instruction
+  ///
+  /// See asm/perf_regs.h for details.
+  @Uint64()
+  external int sampleRegsIntr;
+
+  /// Wakeup watermark for AUX area
+  @Uint32()
+  external int auxWatermark;
+  @Uint16()
+  external int sampleMaxStack;
+  @Uint16()
+  external int reserved2;
+  @Uint32()
+  external int auxSampleSize;
+  @Uint32()
+  external int reserved3;
+
+  /// User provided data if sigtrap=1, passed back to user via
+  /// siginfo_t::si_perf_data, e.g. to permit user to identify the event.
+  /// Note, siginfo_t::si_perf_data is long-sized, and sig_data will be
+  /// truncated accordingly on 32 bit architectures.
+  @Uint64()
+  external int sigData;
+
+  /// Extension of config2
+  @Uint64()
+  external int config3;
+}
+
+/// `enum perf_event_type`: type of the recorded event.
+///
+/// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/include/uapi/linux/perf_event.h#L838
+extension type const EventType(int _) implements int {
+  /// `PERF_RECORD_MMAP`
+  ///
+  /// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/include/uapi/linux/perf_event.h#L879
+  static const mmap = EventType(1);
+
+  /// `PERF_RECORD_SAMPLE`
+  ///
+  /// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/include/uapi/linux/perf_event.h#L947
+  static const sample = EventType(9);
+
+  /// `PERF_RECORD_MMAP2`
+  ///
+  /// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/include/uapi/linux/perf_event.h#L1035
+  static const mmap2 = EventType(10);
+}
+
+/// `enum perf_type_id`
+///
+/// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/include/uapi/linux/perf_event.h#L29
+extension type const TypeId(int index) implements int {
+  static const hardware = TypeId(0);
+  static const software = TypeId(1);
+  static const tracepoint = TypeId(2);
+  static const hwCache = TypeId(3);
+  static const raw = TypeId(4);
+  static const breakpoint = TypeId(5);
+}
+
+/// `enum perf_event_sample_format`: additional information recorded for sample.
+///
+/// Bits that can be set in [PerfEventAttr.sampleType] to request information
+/// in the overflow packets.
+///
+/// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/include/uapi/linux/perf_event.h#L139
+extension type const SampleFormat(int bit) implements int {
+  /// `PERF_SAMPLE_IP`
+  static const ip = SampleFormat(1 << 0);
+
+  /// `PERF_SAMPLE_TID`
+  static const tid = SampleFormat(1 << 1);
+
+  /// `PERF_SAMPLE_TIME`
+  static const time = SampleFormat(1 << 2);
+
+  /// `PERF_SAMPLE_ADDR`
+  static const addr = SampleFormat(1 << 3);
+
+  /// `PERF_SAMPLE_READ`
+  static const read = SampleFormat(1 << 4);
+
+  /// `PERF_SAMPLE_CALLCHAIN`
+  static const callchain = SampleFormat(1 << 5);
+
+  /// `PERF_SAMPLE_ID`
+  static const id = SampleFormat(1 << 6);
+
+  /// `PERF_SAMPLE_CPU`
+  static const cpu = SampleFormat(1 << 7);
+
+  /// `PERF_SAMPLE_PERIOD`
+  static const period = SampleFormat(1 << 8);
+
+  /// `PERF_SAMPLE_STREAM_ID`
+  static const streamId = SampleFormat(1 << 9);
+
+  /// `PERF_SAMPLE_RAW`
+  static const raw = SampleFormat(1 << 10);
+
+  /// `PERF_SAMPLE_BRANCH_STACK`
+  static const branchStack = SampleFormat(1 << 11);
+
+  /// `PERF_SAMPLE_REGS_USER`
+  static const regsUser = SampleFormat(1 << 12);
+
+  /// `PERF_SAMPLE_STACK_USER`
+  static const stackUser = SampleFormat(1 << 13);
+
+  /// `PERF_SAMPLE_WEIGHT`
+  static const weight = SampleFormat(1 << 14);
+
+  /// `PERF_SAMPLE_DATA_SRC`
+  static const dataSrc = SampleFormat(1 << 15);
+
+  /// `PERF_SAMPLE_IDENTIFIER`
+  static const identifier = SampleFormat(1 << 16);
+
+  /// `PERF_SAMPLE_TRANSACTION`
+  static const transaction = SampleFormat(1 << 17);
+
+  /// `PERF_SAMPLE_REGS_INTR`
+  static const regsIntr = SampleFormat(1 << 18);
+
+  /// `PERF_SAMPLE_PHYS_ADDR`
+  static const physAddr = SampleFormat(1 << 19);
+
+  /// `PERF_SAMPLE_AUX`
+  static const aux = SampleFormat(1 << 20);
+
+  /// `PERF_SAMPLE_CGROUP`
+  static const cgroup = SampleFormat(1 << 21);
+
+  /// `PERF_SAMPLE_DATA_PAGE_SIZE`
+  static const dataPageSize = SampleFormat(1 << 22);
+
+  /// `PERF_SAMPLE_CODE_PAGE_SIZE`
+  static const codePageSize = SampleFormat(1 << 23);
+
+  /// `PERF_SAMPLE_WEIGHT_STRUCT`
+  static const weightStruct = SampleFormat(1 << 24);
+
+  static const bitNames = {
+    ip: "ip",
+    tid: "tid",
+    time: "time",
+    addr: "addr",
+    read: "read",
+    callchain: "callchain",
+    id: "id",
+    cpu: "cpu",
+    period: "period",
+    streamId: "streamId",
+    raw: "raw",
+    branchStack: "branchStack",
+    regsUser: "regsUser",
+    stackUser: "stackUser",
+    weight: "weight",
+    dataSrc: "dataSrc",
+    identifier: "identifier",
+    transaction: "transaction",
+    regsIntr: "regsIntr",
+    physAddr: "physAddr",
+    aux: "aux",
+    cgroup: "cgroup",
+    dataPageSize: "dataPageSize",
+    codePageSize: "codePageSize",
+    weightStruct: "weightStruct",
+  };
+
+  static String format(int mask) {
+    return SampleFormat.bitNames.entries
+        .where((e) => (mask & e.key) != 0)
+        .map((e) => e.value)
+        .join('|');
+  }
+}
+
+/// `PERF_RECORD_MMAP`
+///
+/// The `MMAP` events record the `PROT_EXEC` mappings so that we can
+///	correlate userspace `IP`s to code.
+///
+/// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/include/uapi/linux/perf_event.h#L879
+final class MmapEvent extends Struct {
+  external EventHeader header;
+
+  @Uint32()
+  external int pid;
+
+  @Uint32()
+  external int tid;
+
+  @Uint64()
+  external int addr;
+
+  @Uint64()
+  external int len;
+
+  @Uint64()
+  external int pgoffs;
+
+  @Array.variable()
+  external Array<Uint8> filename;
+
+  @override
+  String toString() => 'MmapEvent{addr=${addr.formatAsAddress()},'
+      'len=$len,pgoffs=$pgoffs,'
+      'filename=${filename.toStringFromZeroTerminated()}}';
+}
+
+final class BuildId extends Struct {
+  @Uint8()
+  external int size;
+
+  @Uint8()
+  external int reserved1;
+
+  @Uint16()
+  external int reserved2;
+
+  @Array(20)
+  external Array<Uint8> buildId;
+}
+
+final class Ino extends Struct {
+  @Uint32()
+  external int maj;
+
+  @Uint32()
+  external int min;
+
+  @Uint64()
+  external int ino;
+
+  @Uint64()
+  external int inoGeneration;
+}
+
+final class BuildIdOrIno extends Union {
+  external BuildId buildId;
+  external Ino ino;
+}
+
+/// `PERF_RECORD_MMAP2`
+///
+/// The `MMAP2` records are an augmented version of `MMAP` (see [MapEvent]),
+/// they add `maj`, `min`, `ino` numbers to be used to uniquely identify each
+/// mapping.
+///
+/// https://github.com/torvalds/linux/blob/3e9bff3bbe1355805de919f688bef4baefbfd436/include/uapi/linux/perf_event.h#L1035
+final class Mmap2Event extends Struct {
+  external EventHeader header;
+
+  @Uint32()
+  external int pid;
+
+  @Uint32()
+  external int tid;
+
+  @Uint64()
+  external int addr;
+
+  @Uint64()
+  external int len;
+
+  @Uint64()
+  external int pgoffs;
+
+  external BuildIdOrIno buildIdOrIno;
+
+  @Uint32()
+  external int prot;
+
+  @Uint32()
+  external int flags;
+
+  @Array.variable()
+  external Array<Uint8> filename;
+
+  @override
+  String toString() => 'Mmap2Event{addr=${addr.formatAsAddress()},'
+      'len=$len,pgoffs=$pgoffs,'
+      'filename=${filename.toStringFromZeroTerminated()}}';
+}
+
+extension ArrayToString on Array<Uint8> {
+  String toStringFromFixedLength(int length) =>
+      String.fromCharCodes([for (var i = 0; i < length; i++) this[i]]);
+
+  String toStringFromZeroTerminated() {
+    final sb = StringBuffer();
+    for (var i = 0; this[i] != 0; i++) {
+      sb.writeCharCode(this[i]);
+    }
+    return sb.toString();
+  }
+}
+
+extension FormatAsAddress on int {
+  String formatAsAddress() => toRadixString(16);
+}
+
+const int kb = 1024;
+const int mb = 1024 * kb;
+
+final class StreamingSectionReader {
+  final RandomAccessFile f;
+  final FileSection section;
+
+  final chunk = Uint8List(256 * mb);
+
+  /// Number of bytes available in the chunk.
+  int chunkBytes = 0;
+
+  /// Offset from the start of the section to the start of the chunk.
+  int chunkOffset = 0;
+
+  /// Position within the chunk.
+  int pos = 0;
+
+  StreamingSectionReader(this.f, this.section) {
+    f.setPositionSync(section.offset);
+    refill();
+  }
+
+  bool ensure(int bytes) {
+    if (chunkBytes < (pos + bytes)) {
+      refill();
+    }
+    return chunkBytes >= (pos + bytes);
+  }
+
+  void refill() {
+    final int leftOverBytes = chunkBytes - pos;
+    for (int i = 0; i < leftOverBytes; i++) {
+      chunk[i] = chunk[i + pos];
+    }
+    chunkOffset += pos;
+    pos = 0;
+
+    // Are there any more bytes left to read?
+    if (chunkOffset >= section.size) {
+      chunkBytes = 0;
+      return;
+    }
+
+    print(
+        "processed $chunkOffset bytes of ${section.size} total (${(chunkOffset / section.size * 100).floor()} %)");
+
+    final bytesAlreadyRead = chunkOffset + leftOverBytes;
+    final bytesToRead =
+        math.min(section.size - bytesAlreadyRead, chunk.length - leftOverBytes);
+    final bytesRead =
+        f.readIntoSync(chunk, leftOverBytes, leftOverBytes + bytesToRead);
+    chunkBytes = bytesRead + leftOverBytes;
+  }
+}
+
+final class PerfData {
+  final RandomAccessFile f;
+
+  final Header header;
+
+  PerfData(this.f)
+      : header = Struct.create<Header>(f.readSync(sizeOf<Header>())) {
+    final magic = header.magic.toStringFromFixedLength(8);
+    if (magic != 'PERFILE2') {
+      reportError('Incorrect magic in ${f.path} - $magic');
+    }
+  }
+
+  List<EventAttr> readAttrs() {
+    f.setPositionSync(header.attrs.offset);
+    final attrs = f.readSync(header.attrs.size);
+
+    final result = <EventAttr>[];
+    int pos = 0;
+    while (pos + sizeOf<EventAttr>() < attrs.length) {
+      final attr = Struct.create<EventAttr>(attrs, pos);
+      result.add(attr);
+      pos += attr.size;
+    }
+    return result;
+  }
+
+  Map<OptionalSection, FileSection> readOptionalSectionHeaders() {
+    f.setPositionSync(header.data.offset + header.data.size);
+    final optionalHeaders =
+        f.readSync(sizeOf<FileSection>() * OptionalSection.values.length);
+
+    int headerIndex = 0;
+    return {
+      for (final flag in OptionalSection.values)
+        if (header.flags & (1 << flag.index) != 0)
+          flag: Struct.create<FileSection>(
+              optionalHeaders, sizeOf<FileSection>() * headerIndex++),
+    };
+  }
+
+  late final _dataReader = StreamingSectionReader(f, header.data);
+
+  @pragma('vm:prefer-inline')
+  void readEvents(bool Function(int type, Uint8List chunk, int pos) callback) {
+    final reader = _dataReader;
+
+    while (true) {
+      if (!reader.ensure(sizeOf<EventHeader>())) {
+        // No more events.
+        return;
+      }
+
+      // Note: `reader.ensure` might refill the chunk and invalidate
+      // created struct so extract values eagerly.
+      final EventHeader(:type, :size) =
+          Struct.create<EventHeader>(reader.chunk, reader.pos);
+      if (!reader.ensure(size)) {
+        return;
+      }
+
+      // At this point we are guaranteed to have the whole event in the chunk
+      // starting at `reader.pos`.
+      if (!callback(type, reader.chunk, reader.pos)) {
+        reader.pos += size;
+        return;
+      }
+      reader.pos += size;
+    }
+  }
+
+  Never reportError(String message) => throw ParseError(f.path, message);
+}
+
+final class ParseError extends Error {
+  final String file;
+  final String message;
+
+  ParseError(this.file, this.message);
+
+  @override
+  String toString() => 'Failed to parse $file: $message';
+}
diff --git a/runtime/tools/profiling/lib/src/pprof/generated/profile.pb.dart b/runtime/tools/profiling/lib/src/pprof/generated/profile.pb.dart
new file mode 100644
index 0000000..71199bfd
--- /dev/null
+++ b/runtime/tools/profiling/lib/src/pprof/generated/profile.pb.dart
@@ -0,0 +1,1151 @@
+//
+//  Generated code. Do not modify.
+//  source: profile.proto
+//
+// @dart = 2.12
+
+// ignore_for_file: annotate_overrides, camel_case_types, comment_references
+// ignore_for_file: constant_identifier_names, library_prefixes
+// ignore_for_file: non_constant_identifier_names, prefer_final_fields
+// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
+
+import 'dart:core' as $core;
+
+import 'package:fixnum/fixnum.dart' as $fixnum;
+import 'package:protobuf/protobuf.dart' as $pb;
+
+class Profile extends $pb.GeneratedMessage {
+  factory Profile({
+    $core.Iterable<ValueType>? sampleType,
+    $core.Iterable<Sample>? sample,
+    $core.Iterable<Mapping>? mapping,
+    $core.Iterable<Location>? location,
+    $core.Iterable<Function_>? function,
+    $core.Iterable<$core.String>? stringTable,
+    $fixnum.Int64? dropFrames,
+    $fixnum.Int64? keepFrames,
+    $fixnum.Int64? timeNanos,
+    $fixnum.Int64? durationNanos,
+    ValueType? periodType,
+    $fixnum.Int64? period,
+    $core.Iterable<$fixnum.Int64>? comment,
+    $fixnum.Int64? defaultSampleType,
+  }) {
+    final $result = create();
+    if (sampleType != null) {
+      $result.sampleType.addAll(sampleType);
+    }
+    if (sample != null) {
+      $result.sample.addAll(sample);
+    }
+    if (mapping != null) {
+      $result.mapping.addAll(mapping);
+    }
+    if (location != null) {
+      $result.location.addAll(location);
+    }
+    if (function != null) {
+      $result.function.addAll(function);
+    }
+    if (stringTable != null) {
+      $result.stringTable.addAll(stringTable);
+    }
+    if (dropFrames != null) {
+      $result.dropFrames = dropFrames;
+    }
+    if (keepFrames != null) {
+      $result.keepFrames = keepFrames;
+    }
+    if (timeNanos != null) {
+      $result.timeNanos = timeNanos;
+    }
+    if (durationNanos != null) {
+      $result.durationNanos = durationNanos;
+    }
+    if (periodType != null) {
+      $result.periodType = periodType;
+    }
+    if (period != null) {
+      $result.period = period;
+    }
+    if (comment != null) {
+      $result.comment.addAll(comment);
+    }
+    if (defaultSampleType != null) {
+      $result.defaultSampleType = defaultSampleType;
+    }
+    return $result;
+  }
+  Profile._() : super();
+  factory Profile.fromBuffer($core.List<$core.int> i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromBuffer(i, r);
+  factory Profile.fromJson($core.String i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromJson(i, r);
+
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(
+      _omitMessageNames ? '' : 'Profile',
+      package: const $pb.PackageName(
+          _omitMessageNames ? '' : 'perfetto.third_party.perftools.profiles'),
+      createEmptyInstance: create)
+    ..pc<ValueType>(1, _omitFieldNames ? '' : 'sampleType', $pb.PbFieldType.PM,
+        subBuilder: ValueType.create)
+    ..pc<Sample>(2, _omitFieldNames ? '' : 'sample', $pb.PbFieldType.PM,
+        subBuilder: Sample.create)
+    ..pc<Mapping>(3, _omitFieldNames ? '' : 'mapping', $pb.PbFieldType.PM,
+        subBuilder: Mapping.create)
+    ..pc<Location>(4, _omitFieldNames ? '' : 'location', $pb.PbFieldType.PM,
+        subBuilder: Location.create)
+    ..pc<Function_>(5, _omitFieldNames ? '' : 'function', $pb.PbFieldType.PM,
+        subBuilder: Function_.create)
+    ..pPS(6, _omitFieldNames ? '' : 'stringTable')
+    ..aInt64(7, _omitFieldNames ? '' : 'dropFrames')
+    ..aInt64(8, _omitFieldNames ? '' : 'keepFrames')
+    ..aInt64(9, _omitFieldNames ? '' : 'timeNanos')
+    ..aInt64(10, _omitFieldNames ? '' : 'durationNanos')
+    ..aOM<ValueType>(11, _omitFieldNames ? '' : 'periodType',
+        subBuilder: ValueType.create)
+    ..aInt64(12, _omitFieldNames ? '' : 'period')
+    ..p<$fixnum.Int64>(13, _omitFieldNames ? '' : 'comment', $pb.PbFieldType.K6)
+    ..aInt64(14, _omitFieldNames ? '' : 'defaultSampleType')
+    ..hasRequiredFields = false;
+
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+      'Will be removed in next major version')
+  Profile clone() => Profile()..mergeFromMessage(this);
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+      'Will be removed in next major version')
+  Profile copyWith(void Function(Profile) updates) =>
+      super.copyWith((message) => updates(message as Profile)) as Profile;
+
+  $pb.BuilderInfo get info_ => _i;
+
+  @$core.pragma('dart2js:noInline')
+  static Profile create() => Profile._();
+  Profile createEmptyInstance() => create();
+  static $pb.PbList<Profile> createRepeated() => $pb.PbList<Profile>();
+  @$core.pragma('dart2js:noInline')
+  static Profile getDefault() =>
+      _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Profile>(create);
+  static Profile? _defaultInstance;
+
+  /// A description of the samples associated with each Sample.value.
+  /// For a cpu profile this might be:
+  ///   [["cpu","nanoseconds"]] or [["wall","seconds"]] or [["syscall","count"]]
+  /// For a heap profile, this might be:
+  ///   [["allocations","count"], ["space","bytes"]],
+  /// If one of the values represents the number of events represented
+  /// by the sample, by convention it should be at index 0 and use
+  /// sample_type.unit == "count".
+  @$pb.TagNumber(1)
+  $core.List<ValueType> get sampleType => $_getList(0);
+
+  /// The set of samples recorded in this profile.
+  @$pb.TagNumber(2)
+  $core.List<Sample> get sample => $_getList(1);
+
+  /// Mapping from address ranges to the image/binary/library mapped
+  /// into that address range.  mapping[0] will be the main binary.
+  @$pb.TagNumber(3)
+  $core.List<Mapping> get mapping => $_getList(2);
+
+  /// Useful program location
+  @$pb.TagNumber(4)
+  $core.List<Location> get location => $_getList(3);
+
+  /// Functions referenced by locations
+  @$pb.TagNumber(5)
+  $core.List<Function_> get function => $_getList(4);
+
+  /// A common table for strings referenced by various messages.
+  /// string_table[0] must always be "".
+  @$pb.TagNumber(6)
+  $core.List<$core.String> get stringTable => $_getList(5);
+
+  /// frames with Function.function_name fully matching the following
+  /// regexp will be dropped from the samples, along with their successors.
+  /// Index into string table.
+  @$pb.TagNumber(7)
+  $fixnum.Int64 get dropFrames => $_getI64(6);
+  @$pb.TagNumber(7)
+  set dropFrames($fixnum.Int64 v) {
+    $_setInt64(6, v);
+  }
+
+  @$pb.TagNumber(7)
+  $core.bool hasDropFrames() => $_has(6);
+  @$pb.TagNumber(7)
+  void clearDropFrames() => clearField(7);
+
+  /// frames with Function.function_name fully matching the following
+  /// regexp will be kept, even if it matches drop_functions.
+  /// Index into string table.
+  @$pb.TagNumber(8)
+  $fixnum.Int64 get keepFrames => $_getI64(7);
+  @$pb.TagNumber(8)
+  set keepFrames($fixnum.Int64 v) {
+    $_setInt64(7, v);
+  }
+
+  @$pb.TagNumber(8)
+  $core.bool hasKeepFrames() => $_has(7);
+  @$pb.TagNumber(8)
+  void clearKeepFrames() => clearField(8);
+
+  /// Time of collection (UTC) represented as nanoseconds past the epoch.
+  @$pb.TagNumber(9)
+  $fixnum.Int64 get timeNanos => $_getI64(8);
+  @$pb.TagNumber(9)
+  set timeNanos($fixnum.Int64 v) {
+    $_setInt64(8, v);
+  }
+
+  @$pb.TagNumber(9)
+  $core.bool hasTimeNanos() => $_has(8);
+  @$pb.TagNumber(9)
+  void clearTimeNanos() => clearField(9);
+
+  /// Duration of the profile, if a duration makes sense.
+  @$pb.TagNumber(10)
+  $fixnum.Int64 get durationNanos => $_getI64(9);
+  @$pb.TagNumber(10)
+  set durationNanos($fixnum.Int64 v) {
+    $_setInt64(9, v);
+  }
+
+  @$pb.TagNumber(10)
+  $core.bool hasDurationNanos() => $_has(9);
+  @$pb.TagNumber(10)
+  void clearDurationNanos() => clearField(10);
+
+  /// The kind of events between sampled ocurrences.
+  /// e.g [ "cpu","cycles" ] or [ "heap","bytes" ]
+  @$pb.TagNumber(11)
+  ValueType get periodType => $_getN(10);
+  @$pb.TagNumber(11)
+  set periodType(ValueType v) {
+    setField(11, v);
+  }
+
+  @$pb.TagNumber(11)
+  $core.bool hasPeriodType() => $_has(10);
+  @$pb.TagNumber(11)
+  void clearPeriodType() => clearField(11);
+  @$pb.TagNumber(11)
+  ValueType ensurePeriodType() => $_ensure(10);
+
+  /// The number of events between sampled occurrences.
+  @$pb.TagNumber(12)
+  $fixnum.Int64 get period => $_getI64(11);
+  @$pb.TagNumber(12)
+  set period($fixnum.Int64 v) {
+    $_setInt64(11, v);
+  }
+
+  @$pb.TagNumber(12)
+  $core.bool hasPeriod() => $_has(11);
+  @$pb.TagNumber(12)
+  void clearPeriod() => clearField(12);
+
+  /// Freeform text associated to the profile.
+  /// Indices into string table.
+  @$pb.TagNumber(13)
+  $core.List<$fixnum.Int64> get comment => $_getList(12);
+
+  /// Index into the string table of the type of the preferred sample
+  /// value. If unset, clients should default to the last sample value.
+  @$pb.TagNumber(14)
+  $fixnum.Int64 get defaultSampleType => $_getI64(13);
+  @$pb.TagNumber(14)
+  set defaultSampleType($fixnum.Int64 v) {
+    $_setInt64(13, v);
+  }
+
+  @$pb.TagNumber(14)
+  $core.bool hasDefaultSampleType() => $_has(13);
+  @$pb.TagNumber(14)
+  void clearDefaultSampleType() => clearField(14);
+}
+
+/// ValueType describes the semantics and measurement units of a value.
+class ValueType extends $pb.GeneratedMessage {
+  factory ValueType({
+    $fixnum.Int64? type,
+    $fixnum.Int64? unit,
+  }) {
+    final $result = create();
+    if (type != null) {
+      $result.type = type;
+    }
+    if (unit != null) {
+      $result.unit = unit;
+    }
+    return $result;
+  }
+  ValueType._() : super();
+  factory ValueType.fromBuffer($core.List<$core.int> i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromBuffer(i, r);
+  factory ValueType.fromJson($core.String i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromJson(i, r);
+
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(
+      _omitMessageNames ? '' : 'ValueType',
+      package: const $pb.PackageName(
+          _omitMessageNames ? '' : 'perfetto.third_party.perftools.profiles'),
+      createEmptyInstance: create)
+    ..aInt64(1, _omitFieldNames ? '' : 'type')
+    ..aInt64(2, _omitFieldNames ? '' : 'unit')
+    ..hasRequiredFields = false;
+
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+      'Will be removed in next major version')
+  ValueType clone() => ValueType()..mergeFromMessage(this);
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+      'Will be removed in next major version')
+  ValueType copyWith(void Function(ValueType) updates) =>
+      super.copyWith((message) => updates(message as ValueType)) as ValueType;
+
+  $pb.BuilderInfo get info_ => _i;
+
+  @$core.pragma('dart2js:noInline')
+  static ValueType create() => ValueType._();
+  ValueType createEmptyInstance() => create();
+  static $pb.PbList<ValueType> createRepeated() => $pb.PbList<ValueType>();
+  @$core.pragma('dart2js:noInline')
+  static ValueType getDefault() =>
+      _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ValueType>(create);
+  static ValueType? _defaultInstance;
+
+  /// Index into string table.
+  @$pb.TagNumber(1)
+  $fixnum.Int64 get type => $_getI64(0);
+  @$pb.TagNumber(1)
+  set type($fixnum.Int64 v) {
+    $_setInt64(0, v);
+  }
+
+  @$pb.TagNumber(1)
+  $core.bool hasType() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearType() => clearField(1);
+
+  /// Index into string table.
+  @$pb.TagNumber(2)
+  $fixnum.Int64 get unit => $_getI64(1);
+  @$pb.TagNumber(2)
+  set unit($fixnum.Int64 v) {
+    $_setInt64(1, v);
+  }
+
+  @$pb.TagNumber(2)
+  $core.bool hasUnit() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearUnit() => clearField(2);
+}
+
+/// Each Sample records values encountered in some program
+/// context. The program context is typically a stack trace, perhaps
+/// augmented with auxiliary information like the thread-id, some
+/// indicator of a higher level request being handled etc.
+class Sample extends $pb.GeneratedMessage {
+  factory Sample({
+    $core.Iterable<$fixnum.Int64>? locationId,
+    $core.Iterable<$fixnum.Int64>? value,
+    $core.Iterable<Label>? label,
+  }) {
+    final $result = create();
+    if (locationId != null) {
+      $result.locationId.addAll(locationId);
+    }
+    if (value != null) {
+      $result.value.addAll(value);
+    }
+    if (label != null) {
+      $result.label.addAll(label);
+    }
+    return $result;
+  }
+  Sample._() : super();
+  factory Sample.fromBuffer($core.List<$core.int> i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromBuffer(i, r);
+  factory Sample.fromJson($core.String i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromJson(i, r);
+
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(
+      _omitMessageNames ? '' : 'Sample',
+      package: const $pb.PackageName(
+          _omitMessageNames ? '' : 'perfetto.third_party.perftools.profiles'),
+      createEmptyInstance: create)
+    ..p<$fixnum.Int64>(
+        1, _omitFieldNames ? '' : 'locationId', $pb.PbFieldType.KU6)
+    ..p<$fixnum.Int64>(2, _omitFieldNames ? '' : 'value', $pb.PbFieldType.K6)
+    ..pc<Label>(3, _omitFieldNames ? '' : 'label', $pb.PbFieldType.PM,
+        subBuilder: Label.create)
+    ..hasRequiredFields = false;
+
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+      'Will be removed in next major version')
+  Sample clone() => Sample()..mergeFromMessage(this);
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+      'Will be removed in next major version')
+  Sample copyWith(void Function(Sample) updates) =>
+      super.copyWith((message) => updates(message as Sample)) as Sample;
+
+  $pb.BuilderInfo get info_ => _i;
+
+  @$core.pragma('dart2js:noInline')
+  static Sample create() => Sample._();
+  Sample createEmptyInstance() => create();
+  static $pb.PbList<Sample> createRepeated() => $pb.PbList<Sample>();
+  @$core.pragma('dart2js:noInline')
+  static Sample getDefault() =>
+      _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Sample>(create);
+  static Sample? _defaultInstance;
+
+  /// The ids recorded here correspond to a Profile.location.id.
+  /// The leaf is at location_id[0].
+  @$pb.TagNumber(1)
+  $core.List<$fixnum.Int64> get locationId => $_getList(0);
+
+  /// The type and unit of each value is defined by the corresponding
+  /// entry in Profile.sample_type. All samples must have the same
+  /// number of values, the same as the length of Profile.sample_type.
+  /// When aggregating multiple samples into a single sample, the
+  /// result has a list of values that is the elemntwise sum of the
+  /// lists of the originals.
+  @$pb.TagNumber(2)
+  $core.List<$fixnum.Int64> get value => $_getList(1);
+
+  /// label includes additional context for this sample. It can include
+  /// things like a thread id, allocation size, etc
+  @$pb.TagNumber(3)
+  $core.List<Label> get label => $_getList(2);
+}
+
+class Label extends $pb.GeneratedMessage {
+  factory Label({
+    $fixnum.Int64? key,
+    $fixnum.Int64? str,
+    $fixnum.Int64? num,
+    $fixnum.Int64? numUnit,
+  }) {
+    final $result = create();
+    if (key != null) {
+      $result.key = key;
+    }
+    if (str != null) {
+      $result.str = str;
+    }
+    if (num != null) {
+      $result.num = num;
+    }
+    if (numUnit != null) {
+      $result.numUnit = numUnit;
+    }
+    return $result;
+  }
+  Label._() : super();
+  factory Label.fromBuffer($core.List<$core.int> i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromBuffer(i, r);
+  factory Label.fromJson($core.String i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromJson(i, r);
+
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(
+      _omitMessageNames ? '' : 'Label',
+      package: const $pb.PackageName(
+          _omitMessageNames ? '' : 'perfetto.third_party.perftools.profiles'),
+      createEmptyInstance: create)
+    ..aInt64(1, _omitFieldNames ? '' : 'key')
+    ..aInt64(2, _omitFieldNames ? '' : 'str')
+    ..aInt64(3, _omitFieldNames ? '' : 'num')
+    ..aInt64(4, _omitFieldNames ? '' : 'numUnit')
+    ..hasRequiredFields = false;
+
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+      'Will be removed in next major version')
+  Label clone() => Label()..mergeFromMessage(this);
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+      'Will be removed in next major version')
+  Label copyWith(void Function(Label) updates) =>
+      super.copyWith((message) => updates(message as Label)) as Label;
+
+  $pb.BuilderInfo get info_ => _i;
+
+  @$core.pragma('dart2js:noInline')
+  static Label create() => Label._();
+  Label createEmptyInstance() => create();
+  static $pb.PbList<Label> createRepeated() => $pb.PbList<Label>();
+  @$core.pragma('dart2js:noInline')
+  static Label getDefault() =>
+      _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Label>(create);
+  static Label? _defaultInstance;
+
+  /// Index into string table
+  @$pb.TagNumber(1)
+  $fixnum.Int64 get key => $_getI64(0);
+  @$pb.TagNumber(1)
+  set key($fixnum.Int64 v) {
+    $_setInt64(0, v);
+  }
+
+  @$pb.TagNumber(1)
+  $core.bool hasKey() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearKey() => clearField(1);
+
+  /// Index into string table
+  @$pb.TagNumber(2)
+  $fixnum.Int64 get str => $_getI64(1);
+  @$pb.TagNumber(2)
+  set str($fixnum.Int64 v) {
+    $_setInt64(1, v);
+  }
+
+  @$pb.TagNumber(2)
+  $core.bool hasStr() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearStr() => clearField(2);
+
+  @$pb.TagNumber(3)
+  $fixnum.Int64 get num => $_getI64(2);
+  @$pb.TagNumber(3)
+  set num($fixnum.Int64 v) {
+    $_setInt64(2, v);
+  }
+
+  @$pb.TagNumber(3)
+  $core.bool hasNum() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearNum() => clearField(3);
+
+  /// Index into string table
+  @$pb.TagNumber(4)
+  $fixnum.Int64 get numUnit => $_getI64(3);
+  @$pb.TagNumber(4)
+  set numUnit($fixnum.Int64 v) {
+    $_setInt64(3, v);
+  }
+
+  @$pb.TagNumber(4)
+  $core.bool hasNumUnit() => $_has(3);
+  @$pb.TagNumber(4)
+  void clearNumUnit() => clearField(4);
+}
+
+class Mapping extends $pb.GeneratedMessage {
+  factory Mapping({
+    $fixnum.Int64? id,
+    $fixnum.Int64? memoryStart,
+    $fixnum.Int64? memoryLimit,
+    $fixnum.Int64? fileOffset,
+    $fixnum.Int64? filename,
+    $fixnum.Int64? buildId,
+    $core.bool? hasFunctions,
+    $core.bool? hasFilenames,
+    $core.bool? hasLineNumbers,
+    $core.bool? hasInlineFrames,
+  }) {
+    final $result = create();
+    if (id != null) {
+      $result.id = id;
+    }
+    if (memoryStart != null) {
+      $result.memoryStart = memoryStart;
+    }
+    if (memoryLimit != null) {
+      $result.memoryLimit = memoryLimit;
+    }
+    if (fileOffset != null) {
+      $result.fileOffset = fileOffset;
+    }
+    if (filename != null) {
+      $result.filename = filename;
+    }
+    if (buildId != null) {
+      $result.buildId = buildId;
+    }
+    if (hasFunctions != null) {
+      $result.hasFunctions = hasFunctions;
+    }
+    if (hasFilenames != null) {
+      $result.hasFilenames = hasFilenames;
+    }
+    if (hasLineNumbers != null) {
+      $result.hasLineNumbers = hasLineNumbers;
+    }
+    if (hasInlineFrames != null) {
+      $result.hasInlineFrames = hasInlineFrames;
+    }
+    return $result;
+  }
+  Mapping._() : super();
+  factory Mapping.fromBuffer($core.List<$core.int> i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromBuffer(i, r);
+  factory Mapping.fromJson($core.String i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromJson(i, r);
+
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(
+      _omitMessageNames ? '' : 'Mapping',
+      package: const $pb.PackageName(
+          _omitMessageNames ? '' : 'perfetto.third_party.perftools.profiles'),
+      createEmptyInstance: create)
+    ..a<$fixnum.Int64>(1, _omitFieldNames ? '' : 'id', $pb.PbFieldType.OU6,
+        defaultOrMaker: $fixnum.Int64.ZERO)
+    ..a<$fixnum.Int64>(
+        2, _omitFieldNames ? '' : 'memoryStart', $pb.PbFieldType.OU6,
+        defaultOrMaker: $fixnum.Int64.ZERO)
+    ..a<$fixnum.Int64>(
+        3, _omitFieldNames ? '' : 'memoryLimit', $pb.PbFieldType.OU6,
+        defaultOrMaker: $fixnum.Int64.ZERO)
+    ..a<$fixnum.Int64>(
+        4, _omitFieldNames ? '' : 'fileOffset', $pb.PbFieldType.OU6,
+        defaultOrMaker: $fixnum.Int64.ZERO)
+    ..aInt64(5, _omitFieldNames ? '' : 'filename')
+    ..aInt64(6, _omitFieldNames ? '' : 'buildId')
+    ..aOB(7, _omitFieldNames ? '' : 'hasFunctions')
+    ..aOB(8, _omitFieldNames ? '' : 'hasFilenames')
+    ..aOB(9, _omitFieldNames ? '' : 'hasLineNumbers')
+    ..aOB(10, _omitFieldNames ? '' : 'hasInlineFrames')
+    ..hasRequiredFields = false;
+
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+      'Will be removed in next major version')
+  Mapping clone() => Mapping()..mergeFromMessage(this);
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+      'Will be removed in next major version')
+  Mapping copyWith(void Function(Mapping) updates) =>
+      super.copyWith((message) => updates(message as Mapping)) as Mapping;
+
+  $pb.BuilderInfo get info_ => _i;
+
+  @$core.pragma('dart2js:noInline')
+  static Mapping create() => Mapping._();
+  Mapping createEmptyInstance() => create();
+  static $pb.PbList<Mapping> createRepeated() => $pb.PbList<Mapping>();
+  @$core.pragma('dart2js:noInline')
+  static Mapping getDefault() =>
+      _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Mapping>(create);
+  static Mapping? _defaultInstance;
+
+  /// Unique nonzero id for the mapping.
+  @$pb.TagNumber(1)
+  $fixnum.Int64 get id => $_getI64(0);
+  @$pb.TagNumber(1)
+  set id($fixnum.Int64 v) {
+    $_setInt64(0, v);
+  }
+
+  @$pb.TagNumber(1)
+  $core.bool hasId() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearId() => clearField(1);
+
+  /// Address at which the binary (or DLL) is loaded into memory.
+  @$pb.TagNumber(2)
+  $fixnum.Int64 get memoryStart => $_getI64(1);
+  @$pb.TagNumber(2)
+  set memoryStart($fixnum.Int64 v) {
+    $_setInt64(1, v);
+  }
+
+  @$pb.TagNumber(2)
+  $core.bool hasMemoryStart() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearMemoryStart() => clearField(2);
+
+  /// The limit of the address range occupied by this mapping.
+  @$pb.TagNumber(3)
+  $fixnum.Int64 get memoryLimit => $_getI64(2);
+  @$pb.TagNumber(3)
+  set memoryLimit($fixnum.Int64 v) {
+    $_setInt64(2, v);
+  }
+
+  @$pb.TagNumber(3)
+  $core.bool hasMemoryLimit() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearMemoryLimit() => clearField(3);
+
+  /// Offset in the binary that corresponds to the first mapped address.
+  @$pb.TagNumber(4)
+  $fixnum.Int64 get fileOffset => $_getI64(3);
+  @$pb.TagNumber(4)
+  set fileOffset($fixnum.Int64 v) {
+    $_setInt64(3, v);
+  }
+
+  @$pb.TagNumber(4)
+  $core.bool hasFileOffset() => $_has(3);
+  @$pb.TagNumber(4)
+  void clearFileOffset() => clearField(4);
+
+  /// The object this entry is loaded from.  This can be a filename on
+  /// disk for the main binary and shared libraries, or virtual
+  /// abstractions like "[vdso]".
+  /// Index into string table
+  @$pb.TagNumber(5)
+  $fixnum.Int64 get filename => $_getI64(4);
+  @$pb.TagNumber(5)
+  set filename($fixnum.Int64 v) {
+    $_setInt64(4, v);
+  }
+
+  @$pb.TagNumber(5)
+  $core.bool hasFilename() => $_has(4);
+  @$pb.TagNumber(5)
+  void clearFilename() => clearField(5);
+
+  /// A string that uniquely identifies a particular program version
+  /// with high probability. E.g., for binaries generated by GNU tools,
+  /// it could be the contents of the .note.gnu.build-id field.
+  /// Index into string table
+  @$pb.TagNumber(6)
+  $fixnum.Int64 get buildId => $_getI64(5);
+  @$pb.TagNumber(6)
+  set buildId($fixnum.Int64 v) {
+    $_setInt64(5, v);
+  }
+
+  @$pb.TagNumber(6)
+  $core.bool hasBuildId() => $_has(5);
+  @$pb.TagNumber(6)
+  void clearBuildId() => clearField(6);
+
+  /// The following fields indicate the resolution of symbolic info.
+  @$pb.TagNumber(7)
+  $core.bool get hasFunctions => $_getBF(6);
+  @$pb.TagNumber(7)
+  set hasFunctions($core.bool v) {
+    $_setBool(6, v);
+  }
+
+  @$pb.TagNumber(7)
+  $core.bool hasHasFunctions() => $_has(6);
+  @$pb.TagNumber(7)
+  void clearHasFunctions() => clearField(7);
+
+  @$pb.TagNumber(8)
+  $core.bool get hasFilenames => $_getBF(7);
+  @$pb.TagNumber(8)
+  set hasFilenames($core.bool v) {
+    $_setBool(7, v);
+  }
+
+  @$pb.TagNumber(8)
+  $core.bool hasHasFilenames() => $_has(7);
+  @$pb.TagNumber(8)
+  void clearHasFilenames() => clearField(8);
+
+  @$pb.TagNumber(9)
+  $core.bool get hasLineNumbers => $_getBF(8);
+  @$pb.TagNumber(9)
+  set hasLineNumbers($core.bool v) {
+    $_setBool(8, v);
+  }
+
+  @$pb.TagNumber(9)
+  $core.bool hasHasLineNumbers() => $_has(8);
+  @$pb.TagNumber(9)
+  void clearHasLineNumbers() => clearField(9);
+
+  @$pb.TagNumber(10)
+  $core.bool get hasInlineFrames => $_getBF(9);
+  @$pb.TagNumber(10)
+  set hasInlineFrames($core.bool v) {
+    $_setBool(9, v);
+  }
+
+  @$pb.TagNumber(10)
+  $core.bool hasHasInlineFrames() => $_has(9);
+  @$pb.TagNumber(10)
+  void clearHasInlineFrames() => clearField(10);
+}
+
+/// Describes function and line table debug information.
+class Location extends $pb.GeneratedMessage {
+  factory Location({
+    $fixnum.Int64? id,
+    $fixnum.Int64? mappingId,
+    $fixnum.Int64? address,
+    $core.Iterable<Line>? line,
+    $core.bool? isFolded,
+  }) {
+    final $result = create();
+    if (id != null) {
+      $result.id = id;
+    }
+    if (mappingId != null) {
+      $result.mappingId = mappingId;
+    }
+    if (address != null) {
+      $result.address = address;
+    }
+    if (line != null) {
+      $result.line.addAll(line);
+    }
+    if (isFolded != null) {
+      $result.isFolded = isFolded;
+    }
+    return $result;
+  }
+  Location._() : super();
+  factory Location.fromBuffer($core.List<$core.int> i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromBuffer(i, r);
+  factory Location.fromJson($core.String i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromJson(i, r);
+
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(
+      _omitMessageNames ? '' : 'Location',
+      package: const $pb.PackageName(
+          _omitMessageNames ? '' : 'perfetto.third_party.perftools.profiles'),
+      createEmptyInstance: create)
+    ..a<$fixnum.Int64>(1, _omitFieldNames ? '' : 'id', $pb.PbFieldType.OU6,
+        defaultOrMaker: $fixnum.Int64.ZERO)
+    ..a<$fixnum.Int64>(
+        2, _omitFieldNames ? '' : 'mappingId', $pb.PbFieldType.OU6,
+        defaultOrMaker: $fixnum.Int64.ZERO)
+    ..a<$fixnum.Int64>(3, _omitFieldNames ? '' : 'address', $pb.PbFieldType.OU6,
+        defaultOrMaker: $fixnum.Int64.ZERO)
+    ..pc<Line>(4, _omitFieldNames ? '' : 'line', $pb.PbFieldType.PM,
+        subBuilder: Line.create)
+    ..aOB(5, _omitFieldNames ? '' : 'isFolded')
+    ..hasRequiredFields = false;
+
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+      'Will be removed in next major version')
+  Location clone() => Location()..mergeFromMessage(this);
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+      'Will be removed in next major version')
+  Location copyWith(void Function(Location) updates) =>
+      super.copyWith((message) => updates(message as Location)) as Location;
+
+  $pb.BuilderInfo get info_ => _i;
+
+  @$core.pragma('dart2js:noInline')
+  static Location create() => Location._();
+  Location createEmptyInstance() => create();
+  static $pb.PbList<Location> createRepeated() => $pb.PbList<Location>();
+  @$core.pragma('dart2js:noInline')
+  static Location getDefault() =>
+      _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Location>(create);
+  static Location? _defaultInstance;
+
+  /// Unique nonzero id for the location.  A profile could use
+  /// instruction addresses or any integer sequence as ids.
+  @$pb.TagNumber(1)
+  $fixnum.Int64 get id => $_getI64(0);
+  @$pb.TagNumber(1)
+  set id($fixnum.Int64 v) {
+    $_setInt64(0, v);
+  }
+
+  @$pb.TagNumber(1)
+  $core.bool hasId() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearId() => clearField(1);
+
+  /// The id of the corresponding profile.Mapping for this location.
+  /// It can be unset if the mapping is unknown or not applicable for
+  /// this profile type.
+  @$pb.TagNumber(2)
+  $fixnum.Int64 get mappingId => $_getI64(1);
+  @$pb.TagNumber(2)
+  set mappingId($fixnum.Int64 v) {
+    $_setInt64(1, v);
+  }
+
+  @$pb.TagNumber(2)
+  $core.bool hasMappingId() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearMappingId() => clearField(2);
+
+  /// The instruction address for this location, if available.  It
+  /// should be within [Mapping.memory_start...Mapping.memory_limit]
+  /// for the corresponding mapping. A non-leaf address may be in the
+  /// middle of a call instruction. It is up to display tools to find
+  /// the beginning of the instruction if necessary.
+  @$pb.TagNumber(3)
+  $fixnum.Int64 get address => $_getI64(2);
+  @$pb.TagNumber(3)
+  set address($fixnum.Int64 v) {
+    $_setInt64(2, v);
+  }
+
+  @$pb.TagNumber(3)
+  $core.bool hasAddress() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearAddress() => clearField(3);
+
+  ///  Multiple line indicates this location has inlined functions,
+  ///  where the last entry represents the caller into which the
+  ///  preceding entries were inlined.
+  ///
+  ///  E.g., if memcpy() is inlined into printf:
+  ///     line[0].function_name == "memcpy"
+  ///     line[1].function_name == "printf"
+  @$pb.TagNumber(4)
+  $core.List<Line> get line => $_getList(3);
+
+  /// Provides an indication that multiple symbols map to this location's
+  /// address, for example due to identical code folding by the linker. In that
+  /// case the line information above represents one of the multiple
+  /// symbols. This field must be recomputed when the symbolization state of the
+  /// profile changes.
+  @$pb.TagNumber(5)
+  $core.bool get isFolded => $_getBF(4);
+  @$pb.TagNumber(5)
+  set isFolded($core.bool v) {
+    $_setBool(4, v);
+  }
+
+  @$pb.TagNumber(5)
+  $core.bool hasIsFolded() => $_has(4);
+  @$pb.TagNumber(5)
+  void clearIsFolded() => clearField(5);
+}
+
+class Line extends $pb.GeneratedMessage {
+  factory Line({
+    $fixnum.Int64? functionId,
+    $fixnum.Int64? line,
+  }) {
+    final $result = create();
+    if (functionId != null) {
+      $result.functionId = functionId;
+    }
+    if (line != null) {
+      $result.line = line;
+    }
+    return $result;
+  }
+  Line._() : super();
+  factory Line.fromBuffer($core.List<$core.int> i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromBuffer(i, r);
+  factory Line.fromJson($core.String i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromJson(i, r);
+
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(
+      _omitMessageNames ? '' : 'Line',
+      package: const $pb.PackageName(
+          _omitMessageNames ? '' : 'perfetto.third_party.perftools.profiles'),
+      createEmptyInstance: create)
+    ..a<$fixnum.Int64>(
+        1, _omitFieldNames ? '' : 'functionId', $pb.PbFieldType.OU6,
+        defaultOrMaker: $fixnum.Int64.ZERO)
+    ..aInt64(2, _omitFieldNames ? '' : 'line')
+    ..hasRequiredFields = false;
+
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+      'Will be removed in next major version')
+  Line clone() => Line()..mergeFromMessage(this);
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+      'Will be removed in next major version')
+  Line copyWith(void Function(Line) updates) =>
+      super.copyWith((message) => updates(message as Line)) as Line;
+
+  $pb.BuilderInfo get info_ => _i;
+
+  @$core.pragma('dart2js:noInline')
+  static Line create() => Line._();
+  Line createEmptyInstance() => create();
+  static $pb.PbList<Line> createRepeated() => $pb.PbList<Line>();
+  @$core.pragma('dart2js:noInline')
+  static Line getDefault() =>
+      _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Line>(create);
+  static Line? _defaultInstance;
+
+  /// The id of the corresponding profile.Function for this line.
+  @$pb.TagNumber(1)
+  $fixnum.Int64 get functionId => $_getI64(0);
+  @$pb.TagNumber(1)
+  set functionId($fixnum.Int64 v) {
+    $_setInt64(0, v);
+  }
+
+  @$pb.TagNumber(1)
+  $core.bool hasFunctionId() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearFunctionId() => clearField(1);
+
+  /// Line number in source code.
+  @$pb.TagNumber(2)
+  $fixnum.Int64 get line => $_getI64(1);
+  @$pb.TagNumber(2)
+  set line($fixnum.Int64 v) {
+    $_setInt64(1, v);
+  }
+
+  @$pb.TagNumber(2)
+  $core.bool hasLine() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearLine() => clearField(2);
+}
+
+class Function_ extends $pb.GeneratedMessage {
+  factory Function_({
+    $fixnum.Int64? id,
+    $fixnum.Int64? name,
+    $fixnum.Int64? systemName,
+    $fixnum.Int64? filename,
+    $fixnum.Int64? startLine,
+  }) {
+    final $result = create();
+    if (id != null) {
+      $result.id = id;
+    }
+    if (name != null) {
+      $result.name = name;
+    }
+    if (systemName != null) {
+      $result.systemName = systemName;
+    }
+    if (filename != null) {
+      $result.filename = filename;
+    }
+    if (startLine != null) {
+      $result.startLine = startLine;
+    }
+    return $result;
+  }
+  Function_._() : super();
+  factory Function_.fromBuffer($core.List<$core.int> i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromBuffer(i, r);
+  factory Function_.fromJson($core.String i,
+          [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
+      create()..mergeFromJson(i, r);
+
+  static final $pb.BuilderInfo _i = $pb.BuilderInfo(
+      _omitMessageNames ? '' : 'Function',
+      package: const $pb.PackageName(
+          _omitMessageNames ? '' : 'perfetto.third_party.perftools.profiles'),
+      createEmptyInstance: create)
+    ..a<$fixnum.Int64>(1, _omitFieldNames ? '' : 'id', $pb.PbFieldType.OU6,
+        defaultOrMaker: $fixnum.Int64.ZERO)
+    ..aInt64(2, _omitFieldNames ? '' : 'name')
+    ..aInt64(3, _omitFieldNames ? '' : 'systemName')
+    ..aInt64(4, _omitFieldNames ? '' : 'filename')
+    ..aInt64(5, _omitFieldNames ? '' : 'startLine')
+    ..hasRequiredFields = false;
+
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
+      'Will be removed in next major version')
+  Function_ clone() => Function_()..mergeFromMessage(this);
+  @$core.Deprecated('Using this can add significant overhead to your binary. '
+      'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
+      'Will be removed in next major version')
+  Function_ copyWith(void Function(Function_) updates) =>
+      super.copyWith((message) => updates(message as Function_)) as Function_;
+
+  $pb.BuilderInfo get info_ => _i;
+
+  @$core.pragma('dart2js:noInline')
+  static Function_ create() => Function_._();
+  Function_ createEmptyInstance() => create();
+  static $pb.PbList<Function_> createRepeated() => $pb.PbList<Function_>();
+  @$core.pragma('dart2js:noInline')
+  static Function_ getDefault() =>
+      _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Function_>(create);
+  static Function_? _defaultInstance;
+
+  /// Unique nonzero id for the function.
+  @$pb.TagNumber(1)
+  $fixnum.Int64 get id => $_getI64(0);
+  @$pb.TagNumber(1)
+  set id($fixnum.Int64 v) {
+    $_setInt64(0, v);
+  }
+
+  @$pb.TagNumber(1)
+  $core.bool hasId() => $_has(0);
+  @$pb.TagNumber(1)
+  void clearId() => clearField(1);
+
+  /// Name of the function, in human-readable form if available.
+  /// Index into string table
+  @$pb.TagNumber(2)
+  $fixnum.Int64 get name => $_getI64(1);
+  @$pb.TagNumber(2)
+  set name($fixnum.Int64 v) {
+    $_setInt64(1, v);
+  }
+
+  @$pb.TagNumber(2)
+  $core.bool hasName() => $_has(1);
+  @$pb.TagNumber(2)
+  void clearName() => clearField(2);
+
+  /// Name of the function, as identified by the system.
+  /// For instance, it can be a C++ mangled name.
+  /// Index into string table
+  @$pb.TagNumber(3)
+  $fixnum.Int64 get systemName => $_getI64(2);
+  @$pb.TagNumber(3)
+  set systemName($fixnum.Int64 v) {
+    $_setInt64(2, v);
+  }
+
+  @$pb.TagNumber(3)
+  $core.bool hasSystemName() => $_has(2);
+  @$pb.TagNumber(3)
+  void clearSystemName() => clearField(3);
+
+  /// Source file containing the function.
+  /// Index into string table
+  @$pb.TagNumber(4)
+  $fixnum.Int64 get filename => $_getI64(3);
+  @$pb.TagNumber(4)
+  set filename($fixnum.Int64 v) {
+    $_setInt64(3, v);
+  }
+
+  @$pb.TagNumber(4)
+  $core.bool hasFilename() => $_has(3);
+  @$pb.TagNumber(4)
+  void clearFilename() => clearField(4);
+
+  /// Line number in source file.
+  @$pb.TagNumber(5)
+  $fixnum.Int64 get startLine => $_getI64(4);
+  @$pb.TagNumber(5)
+  set startLine($fixnum.Int64 v) {
+    $_setInt64(4, v);
+  }
+
+  @$pb.TagNumber(5)
+  $core.bool hasStartLine() => $_has(4);
+  @$pb.TagNumber(5)
+  void clearStartLine() => clearField(5);
+}
+
+const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names');
+const _omitMessageNames =
+    $core.bool.fromEnvironment('protobuf.omit_message_names');
diff --git a/runtime/tools/profiling/lib/src/pprof/generated/profile.pbenum.dart b/runtime/tools/profiling/lib/src/pprof/generated/profile.pbenum.dart
new file mode 100644
index 0000000..f04e4f1
--- /dev/null
+++ b/runtime/tools/profiling/lib/src/pprof/generated/profile.pbenum.dart
@@ -0,0 +1,10 @@
+//
+//  Generated code. Do not modify.
+//  source: profile.proto
+//
+// @dart = 2.12
+
+// ignore_for_file: annotate_overrides, camel_case_types, comment_references
+// ignore_for_file: constant_identifier_names, library_prefixes
+// ignore_for_file: non_constant_identifier_names, prefer_final_fields
+// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
diff --git a/runtime/tools/profiling/lib/src/pprof/generated/profile.pbjson.dart b/runtime/tools/profiling/lib/src/pprof/generated/profile.pbjson.dart
new file mode 100644
index 0000000..dbb0cd4
--- /dev/null
+++ b/runtime/tools/profiling/lib/src/pprof/generated/profile.pbjson.dart
@@ -0,0 +1,243 @@
+//
+//  Generated code. Do not modify.
+//  source: profile.proto
+//
+// @dart = 2.12
+
+// ignore_for_file: annotate_overrides, camel_case_types, comment_references
+// ignore_for_file: constant_identifier_names, library_prefixes
+// ignore_for_file: non_constant_identifier_names, prefer_final_fields
+// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
+
+import 'dart:convert' as $convert;
+import 'dart:core' as $core;
+import 'dart:typed_data' as $typed_data;
+
+@$core.Deprecated('Use profileDescriptor instead')
+const Profile$json = {
+  '1': 'Profile',
+  '2': [
+    {
+      '1': 'sample_type',
+      '3': 1,
+      '4': 3,
+      '5': 11,
+      '6': '.perfetto.third_party.perftools.profiles.ValueType',
+      '10': 'sampleType'
+    },
+    {
+      '1': 'sample',
+      '3': 2,
+      '4': 3,
+      '5': 11,
+      '6': '.perfetto.third_party.perftools.profiles.Sample',
+      '10': 'sample'
+    },
+    {
+      '1': 'mapping',
+      '3': 3,
+      '4': 3,
+      '5': 11,
+      '6': '.perfetto.third_party.perftools.profiles.Mapping',
+      '10': 'mapping'
+    },
+    {
+      '1': 'location',
+      '3': 4,
+      '4': 3,
+      '5': 11,
+      '6': '.perfetto.third_party.perftools.profiles.Location',
+      '10': 'location'
+    },
+    {
+      '1': 'function',
+      '3': 5,
+      '4': 3,
+      '5': 11,
+      '6': '.perfetto.third_party.perftools.profiles.Function',
+      '10': 'function'
+    },
+    {'1': 'string_table', '3': 6, '4': 3, '5': 9, '10': 'stringTable'},
+    {'1': 'drop_frames', '3': 7, '4': 1, '5': 3, '10': 'dropFrames'},
+    {'1': 'keep_frames', '3': 8, '4': 1, '5': 3, '10': 'keepFrames'},
+    {'1': 'time_nanos', '3': 9, '4': 1, '5': 3, '10': 'timeNanos'},
+    {'1': 'duration_nanos', '3': 10, '4': 1, '5': 3, '10': 'durationNanos'},
+    {
+      '1': 'period_type',
+      '3': 11,
+      '4': 1,
+      '5': 11,
+      '6': '.perfetto.third_party.perftools.profiles.ValueType',
+      '10': 'periodType'
+    },
+    {'1': 'period', '3': 12, '4': 1, '5': 3, '10': 'period'},
+    {'1': 'comment', '3': 13, '4': 3, '5': 3, '10': 'comment'},
+    {
+      '1': 'default_sample_type',
+      '3': 14,
+      '4': 1,
+      '5': 3,
+      '10': 'defaultSampleType'
+    },
+  ],
+};
+
+/// Descriptor for `Profile`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List profileDescriptor = $convert.base64Decode(
+    'CgdQcm9maWxlElMKC3NhbXBsZV90eXBlGAEgAygLMjIucGVyZmV0dG8udGhpcmRfcGFydHkucG'
+    'VyZnRvb2xzLnByb2ZpbGVzLlZhbHVlVHlwZVIKc2FtcGxlVHlwZRJHCgZzYW1wbGUYAiADKAsy'
+    'Ly5wZXJmZXR0by50aGlyZF9wYXJ0eS5wZXJmdG9vbHMucHJvZmlsZXMuU2FtcGxlUgZzYW1wbG'
+    'USSgoHbWFwcGluZxgDIAMoCzIwLnBlcmZldHRvLnRoaXJkX3BhcnR5LnBlcmZ0b29scy5wcm9m'
+    'aWxlcy5NYXBwaW5nUgdtYXBwaW5nEk0KCGxvY2F0aW9uGAQgAygLMjEucGVyZmV0dG8udGhpcm'
+    'RfcGFydHkucGVyZnRvb2xzLnByb2ZpbGVzLkxvY2F0aW9uUghsb2NhdGlvbhJNCghmdW5jdGlv'
+    'bhgFIAMoCzIxLnBlcmZldHRvLnRoaXJkX3BhcnR5LnBlcmZ0b29scy5wcm9maWxlcy5GdW5jdG'
+    'lvblIIZnVuY3Rpb24SIQoMc3RyaW5nX3RhYmxlGAYgAygJUgtzdHJpbmdUYWJsZRIfCgtkcm9w'
+    'X2ZyYW1lcxgHIAEoA1IKZHJvcEZyYW1lcxIfCgtrZWVwX2ZyYW1lcxgIIAEoA1IKa2VlcEZyYW'
+    '1lcxIdCgp0aW1lX25hbm9zGAkgASgDUgl0aW1lTmFub3MSJQoOZHVyYXRpb25fbmFub3MYCiAB'
+    'KANSDWR1cmF0aW9uTmFub3MSUwoLcGVyaW9kX3R5cGUYCyABKAsyMi5wZXJmZXR0by50aGlyZF'
+    '9wYXJ0eS5wZXJmdG9vbHMucHJvZmlsZXMuVmFsdWVUeXBlUgpwZXJpb2RUeXBlEhYKBnBlcmlv'
+    'ZBgMIAEoA1IGcGVyaW9kEhgKB2NvbW1lbnQYDSADKANSB2NvbW1lbnQSLgoTZGVmYXVsdF9zYW'
+    '1wbGVfdHlwZRgOIAEoA1IRZGVmYXVsdFNhbXBsZVR5cGU=');
+
+@$core.Deprecated('Use valueTypeDescriptor instead')
+const ValueType$json = {
+  '1': 'ValueType',
+  '2': [
+    {'1': 'type', '3': 1, '4': 1, '5': 3, '10': 'type'},
+    {'1': 'unit', '3': 2, '4': 1, '5': 3, '10': 'unit'},
+  ],
+};
+
+/// Descriptor for `ValueType`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List valueTypeDescriptor = $convert.base64Decode(
+    'CglWYWx1ZVR5cGUSEgoEdHlwZRgBIAEoA1IEdHlwZRISCgR1bml0GAIgASgDUgR1bml0');
+
+@$core.Deprecated('Use sampleDescriptor instead')
+const Sample$json = {
+  '1': 'Sample',
+  '2': [
+    {'1': 'location_id', '3': 1, '4': 3, '5': 4, '10': 'locationId'},
+    {'1': 'value', '3': 2, '4': 3, '5': 3, '10': 'value'},
+    {
+      '1': 'label',
+      '3': 3,
+      '4': 3,
+      '5': 11,
+      '6': '.perfetto.third_party.perftools.profiles.Label',
+      '10': 'label'
+    },
+  ],
+};
+
+/// Descriptor for `Sample`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List sampleDescriptor = $convert.base64Decode(
+    'CgZTYW1wbGUSHwoLbG9jYXRpb25faWQYASADKARSCmxvY2F0aW9uSWQSFAoFdmFsdWUYAiADKA'
+    'NSBXZhbHVlEkQKBWxhYmVsGAMgAygLMi4ucGVyZmV0dG8udGhpcmRfcGFydHkucGVyZnRvb2xz'
+    'LnByb2ZpbGVzLkxhYmVsUgVsYWJlbA==');
+
+@$core.Deprecated('Use labelDescriptor instead')
+const Label$json = {
+  '1': 'Label',
+  '2': [
+    {'1': 'key', '3': 1, '4': 1, '5': 3, '10': 'key'},
+    {'1': 'str', '3': 2, '4': 1, '5': 3, '10': 'str'},
+    {'1': 'num', '3': 3, '4': 1, '5': 3, '10': 'num'},
+    {'1': 'num_unit', '3': 4, '4': 1, '5': 3, '10': 'numUnit'},
+  ],
+};
+
+/// Descriptor for `Label`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List labelDescriptor = $convert.base64Decode(
+    'CgVMYWJlbBIQCgNrZXkYASABKANSA2tleRIQCgNzdHIYAiABKANSA3N0chIQCgNudW0YAyABKA'
+    'NSA251bRIZCghudW1fdW5pdBgEIAEoA1IHbnVtVW5pdA==');
+
+@$core.Deprecated('Use mappingDescriptor instead')
+const Mapping$json = {
+  '1': 'Mapping',
+  '2': [
+    {'1': 'id', '3': 1, '4': 1, '5': 4, '10': 'id'},
+    {'1': 'memory_start', '3': 2, '4': 1, '5': 4, '10': 'memoryStart'},
+    {'1': 'memory_limit', '3': 3, '4': 1, '5': 4, '10': 'memoryLimit'},
+    {'1': 'file_offset', '3': 4, '4': 1, '5': 4, '10': 'fileOffset'},
+    {'1': 'filename', '3': 5, '4': 1, '5': 3, '10': 'filename'},
+    {'1': 'build_id', '3': 6, '4': 1, '5': 3, '10': 'buildId'},
+    {'1': 'has_functions', '3': 7, '4': 1, '5': 8, '10': 'hasFunctions'},
+    {'1': 'has_filenames', '3': 8, '4': 1, '5': 8, '10': 'hasFilenames'},
+    {'1': 'has_line_numbers', '3': 9, '4': 1, '5': 8, '10': 'hasLineNumbers'},
+    {
+      '1': 'has_inline_frames',
+      '3': 10,
+      '4': 1,
+      '5': 8,
+      '10': 'hasInlineFrames'
+    },
+  ],
+};
+
+/// Descriptor for `Mapping`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List mappingDescriptor = $convert.base64Decode(
+    'CgdNYXBwaW5nEg4KAmlkGAEgASgEUgJpZBIhCgxtZW1vcnlfc3RhcnQYAiABKARSC21lbW9yeV'
+    'N0YXJ0EiEKDG1lbW9yeV9saW1pdBgDIAEoBFILbWVtb3J5TGltaXQSHwoLZmlsZV9vZmZzZXQY'
+    'BCABKARSCmZpbGVPZmZzZXQSGgoIZmlsZW5hbWUYBSABKANSCGZpbGVuYW1lEhkKCGJ1aWxkX2'
+    'lkGAYgASgDUgdidWlsZElkEiMKDWhhc19mdW5jdGlvbnMYByABKAhSDGhhc0Z1bmN0aW9ucxIj'
+    'Cg1oYXNfZmlsZW5hbWVzGAggASgIUgxoYXNGaWxlbmFtZXMSKAoQaGFzX2xpbmVfbnVtYmVycx'
+    'gJIAEoCFIOaGFzTGluZU51bWJlcnMSKgoRaGFzX2lubGluZV9mcmFtZXMYCiABKAhSD2hhc0lu'
+    'bGluZUZyYW1lcw==');
+
+@$core.Deprecated('Use locationDescriptor instead')
+const Location$json = {
+  '1': 'Location',
+  '2': [
+    {'1': 'id', '3': 1, '4': 1, '5': 4, '10': 'id'},
+    {'1': 'mapping_id', '3': 2, '4': 1, '5': 4, '10': 'mappingId'},
+    {'1': 'address', '3': 3, '4': 1, '5': 4, '10': 'address'},
+    {
+      '1': 'line',
+      '3': 4,
+      '4': 3,
+      '5': 11,
+      '6': '.perfetto.third_party.perftools.profiles.Line',
+      '10': 'line'
+    },
+    {'1': 'is_folded', '3': 5, '4': 1, '5': 8, '10': 'isFolded'},
+  ],
+};
+
+/// Descriptor for `Location`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List locationDescriptor = $convert.base64Decode(
+    'CghMb2NhdGlvbhIOCgJpZBgBIAEoBFICaWQSHQoKbWFwcGluZ19pZBgCIAEoBFIJbWFwcGluZ0'
+    'lkEhgKB2FkZHJlc3MYAyABKARSB2FkZHJlc3MSQQoEbGluZRgEIAMoCzItLnBlcmZldHRvLnRo'
+    'aXJkX3BhcnR5LnBlcmZ0b29scy5wcm9maWxlcy5MaW5lUgRsaW5lEhsKCWlzX2ZvbGRlZBgFIA'
+    'EoCFIIaXNGb2xkZWQ=');
+
+@$core.Deprecated('Use lineDescriptor instead')
+const Line$json = {
+  '1': 'Line',
+  '2': [
+    {'1': 'function_id', '3': 1, '4': 1, '5': 4, '10': 'functionId'},
+    {'1': 'line', '3': 2, '4': 1, '5': 3, '10': 'line'},
+  ],
+};
+
+/// Descriptor for `Line`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List lineDescriptor = $convert.base64Decode(
+    'CgRMaW5lEh8KC2Z1bmN0aW9uX2lkGAEgASgEUgpmdW5jdGlvbklkEhIKBGxpbmUYAiABKANSBG'
+    'xpbmU=');
+
+@$core.Deprecated('Use function_Descriptor instead')
+const Function_$json = {
+  '1': 'Function',
+  '2': [
+    {'1': 'id', '3': 1, '4': 1, '5': 4, '10': 'id'},
+    {'1': 'name', '3': 2, '4': 1, '5': 3, '10': 'name'},
+    {'1': 'system_name', '3': 3, '4': 1, '5': 3, '10': 'systemName'},
+    {'1': 'filename', '3': 4, '4': 1, '5': 3, '10': 'filename'},
+    {'1': 'start_line', '3': 5, '4': 1, '5': 3, '10': 'startLine'},
+  ],
+};
+
+/// Descriptor for `Function`. Decode as a `google.protobuf.DescriptorProto`.
+final $typed_data.Uint8List function_Descriptor = $convert.base64Decode(
+    'CghGdW5jdGlvbhIOCgJpZBgBIAEoBFICaWQSEgoEbmFtZRgCIAEoA1IEbmFtZRIfCgtzeXN0ZW'
+    '1fbmFtZRgDIAEoA1IKc3lzdGVtTmFtZRIaCghmaWxlbmFtZRgEIAEoA1IIZmlsZW5hbWUSHQoK'
+    'c3RhcnRfbGluZRgFIAEoA1IJc3RhcnRMaW5l');
diff --git a/runtime/tools/profiling/lib/src/pprof/generated/profile.pbserver.dart b/runtime/tools/profiling/lib/src/pprof/generated/profile.pbserver.dart
new file mode 100644
index 0000000..e81bff6
--- /dev/null
+++ b/runtime/tools/profiling/lib/src/pprof/generated/profile.pbserver.dart
@@ -0,0 +1,13 @@
+//
+//  Generated code. Do not modify.
+//  source: profile.proto
+//
+// @dart = 2.12
+
+// ignore_for_file: annotate_overrides, camel_case_types, comment_references
+// ignore_for_file: constant_identifier_names
+// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes
+// ignore_for_file: non_constant_identifier_names, prefer_final_fields
+// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
+
+export 'profile.pb.dart';
diff --git a/runtime/tools/profiling/lib/src/pprof/profile.proto b/runtime/tools/profiling/lib/src/pprof/profile.proto
new file mode 100644
index 0000000..21282c5
--- /dev/null
+++ b/runtime/tools/profiling/lib/src/pprof/profile.proto
@@ -0,0 +1,230 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Profile is a common stacktrace profile format.
+//
+// Measurements represented with this format should follow the
+// following conventions:
+//
+// - Consumers should treat unset optional fields as if they had been
+//   set with their default value.
+//
+// - When possible, measurements should be stored in "unsampled" form
+//   that is most useful to humans.  There should be enough
+//   information present to determine the original sampled values.
+//
+// - On-disk, the serialized proto must be gzip-compressed.
+//
+// - The profile is represented as a set of samples, where each sample
+//   references a sequence of locations, and where each location belongs
+//   to a mapping.
+// - There is a N->1 relationship from sample.location_id entries to
+//   locations. For every sample.location_id entry there must be a
+//   unique Location with that id.
+// - There is an optional N->1 relationship from locations to
+//   mappings. For every nonzero Location.mapping_id there must be a
+//   unique Mapping with that id.
+
+syntax = "proto3";
+
+// This is in perfetto.third_party to avoid clashing with potential other
+// copies of this proto.
+package perfetto.third_party.perftools.profiles;
+
+option java_package = "com.google.perftools.profiles";
+option java_outer_classname = "ProfileProto";
+
+message Profile {
+  // A description of the samples associated with each Sample.value.
+  // For a cpu profile this might be:
+  //   [["cpu","nanoseconds"]] or [["wall","seconds"]] or [["syscall","count"]]
+  // For a heap profile, this might be:
+  //   [["allocations","count"], ["space","bytes"]],
+  // If one of the values represents the number of events represented
+  // by the sample, by convention it should be at index 0 and use
+  // sample_type.unit == "count".
+  repeated ValueType sample_type = 1;
+  // The set of samples recorded in this profile.
+  repeated Sample sample = 2;
+  // Mapping from address ranges to the image/binary/library mapped
+  // into that address range.  mapping[0] will be the main binary.
+  repeated Mapping mapping = 3;
+  // Useful program location
+  repeated Location location = 4;
+  // Functions referenced by locations
+  repeated Function function = 5;
+  // A common table for strings referenced by various messages.
+  // string_table[0] must always be "".
+  repeated string string_table = 6;
+  // frames with Function.function_name fully matching the following
+  // regexp will be dropped from the samples, along with their successors.
+  // Index into string table.
+  int64 drop_frames = 7;
+  // frames with Function.function_name fully matching the following
+  // regexp will be kept, even if it matches drop_functions.
+  // Index into string table.
+  int64 keep_frames = 8;
+
+  // The following fields are informational, do not affect
+  // interpretation of results.
+
+  // Time of collection (UTC) represented as nanoseconds past the epoch.
+  int64 time_nanos = 9;
+  // Duration of the profile, if a duration makes sense.
+  int64 duration_nanos = 10;
+  // The kind of events between sampled ocurrences.
+  // e.g [ "cpu","cycles" ] or [ "heap","bytes" ]
+  ValueType period_type = 11;
+  // The number of events between sampled occurrences.
+  int64 period = 12;
+  // Freeform text associated to the profile.
+  // Indices into string table.
+  repeated int64 comment = 13;
+  // Index into the string table of the type of the preferred sample
+  // value. If unset, clients should default to the last sample value.
+  int64 default_sample_type = 14;
+}
+
+// ValueType describes the semantics and measurement units of a value.
+message ValueType {
+  // Index into string table.
+  int64 type = 1;
+  // Index into string table.
+  int64 unit = 2;
+}
+
+// Each Sample records values encountered in some program
+// context. The program context is typically a stack trace, perhaps
+// augmented with auxiliary information like the thread-id, some
+// indicator of a higher level request being handled etc.
+message Sample {
+  // The ids recorded here correspond to a Profile.location.id.
+  // The leaf is at location_id[0].
+  repeated uint64 location_id = 1;
+  // The type and unit of each value is defined by the corresponding
+  // entry in Profile.sample_type. All samples must have the same
+  // number of values, the same as the length of Profile.sample_type.
+  // When aggregating multiple samples into a single sample, the
+  // result has a list of values that is the elemntwise sum of the
+  // lists of the originals.
+  repeated int64 value = 2;
+  // label includes additional context for this sample. It can include
+  // things like a thread id, allocation size, etc
+  repeated Label label = 3;
+}
+
+message Label {
+  // Index into string table
+  int64 key = 1;
+
+  // At most one of the following must be present
+
+  // Index into string table
+  int64 str = 2;
+
+  int64 num = 3;
+
+  // Should only be present when num is present.
+  // Specifies the units of num.
+  // Use arbitrary string (for example, "requests") as a custom count unit.
+  // If no unit is specified, consumer may apply heuristic to deduce the unit.
+  // Consumers may also  interpret units like "bytes" and "kilobytes" as memory
+  // units and units like "seconds" and "nanoseconds" as time units,
+  // and apply appropriate unit conversions to these.
+
+  // Index into string table
+  int64 num_unit = 4;
+}
+
+message Mapping {
+  // Unique nonzero id for the mapping.
+  uint64 id = 1;
+  // Address at which the binary (or DLL) is loaded into memory.
+  uint64 memory_start = 2;
+  // The limit of the address range occupied by this mapping.
+  uint64 memory_limit = 3;
+  // Offset in the binary that corresponds to the first mapped address.
+  uint64 file_offset = 4;
+  // The object this entry is loaded from.  This can be a filename on
+  // disk for the main binary and shared libraries, or virtual
+  // abstractions like "[vdso]".
+  // Index into string table
+  int64 filename = 5;
+  // A string that uniquely identifies a particular program version
+  // with high probability. E.g., for binaries generated by GNU tools,
+  // it could be the contents of the .note.gnu.build-id field.
+  // Index into string table
+  int64 build_id = 6;
+
+  // The following fields indicate the resolution of symbolic info.
+  bool has_functions = 7;
+  bool has_filenames = 8;
+  bool has_line_numbers = 9;
+  bool has_inline_frames = 10;
+}
+
+// Describes function and line table debug information.
+message Location {
+  // Unique nonzero id for the location.  A profile could use
+  // instruction addresses or any integer sequence as ids.
+  uint64 id = 1;
+  // The id of the corresponding profile.Mapping for this location.
+  // It can be unset if the mapping is unknown or not applicable for
+  // this profile type.
+  uint64 mapping_id = 2;
+  // The instruction address for this location, if available.  It
+  // should be within [Mapping.memory_start...Mapping.memory_limit]
+  // for the corresponding mapping. A non-leaf address may be in the
+  // middle of a call instruction. It is up to display tools to find
+  // the beginning of the instruction if necessary.
+  uint64 address = 3;
+  // Multiple line indicates this location has inlined functions,
+  // where the last entry represents the caller into which the
+  // preceding entries were inlined.
+  //
+  // E.g., if memcpy() is inlined into printf:
+  //    line[0].function_name == "memcpy"
+  //    line[1].function_name == "printf"
+  repeated Line line = 4;
+  // Provides an indication that multiple symbols map to this location's
+  // address, for example due to identical code folding by the linker. In that
+  // case the line information above represents one of the multiple
+  // symbols. This field must be recomputed when the symbolization state of the
+  // profile changes.
+  bool is_folded = 5;
+}
+
+message Line {
+  // The id of the corresponding profile.Function for this line.
+  uint64 function_id = 1;
+  // Line number in source code.
+  int64 line = 2;
+}
+
+message Function {
+  // Unique nonzero id for the function.
+  uint64 id = 1;
+  // Name of the function, in human-readable form if available.
+  // Index into string table
+  int64 name = 2;
+  // Name of the function, as identified by the system.
+  // For instance, it can be a C++ mangled name.
+  // Index into string table
+  int64 system_name = 3;
+  // Source file containing the function.
+  // Index into string table
+  int64 filename = 4;
+  // Line number in source file.
+  int64 start_line = 5;
+}
diff --git a/runtime/tools/profiling/lib/src/symbols.dart b/runtime/tools/profiling/lib/src/symbols.dart
new file mode 100644
index 0000000..03dbd8ef
--- /dev/null
+++ b/runtime/tools/profiling/lib/src/symbols.dart
@@ -0,0 +1,72 @@
+// Copyright (c) 2024, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:typed_data';
+
+import 'package:profiling/src/elf_utils.dart';
+
+/// Symbols of a TEXT section of a binary indexed by their file offset.
+class Symbols {
+  final Uint32List fileOffsets;
+  final List<String> names;
+
+  Symbols._(this.fileOffsets, this.names);
+
+  /// Given the [fileOffset] find a symbol it falls into.
+  ///
+  /// We assume that symbol with index `i` starts at `fileOffsets[i]`
+  /// and ends at `fileOffset[i+1]`.
+  int? symbolIndex(int fileOffset) {
+    int lo = 0;
+    int hi = fileOffsets.length - 1;
+    while (lo <= hi) {
+      int mid = ((hi - lo + 1) >> 1) + lo;
+      if (fileOffset < fileOffsets[mid]) {
+        hi = mid - 1;
+      } else if ((mid != hi) && (fileOffset >= fileOffsets[mid + 1])) {
+        lo = mid + 1;
+      } else {
+        return mid;
+      }
+    }
+    return null;
+  }
+
+  String? lookupName(int fileOffset) {
+    final index = symbolIndex(fileOffset);
+    return index != null ? names[index] : null;
+  }
+
+  /// Try loading symbols from the binary at [path].
+  static Symbols? load(String path) {
+    try {
+      final loadingBias = loadingBiasOf(path);
+      final symbols = textSymbolsOf(path).toList(growable: false);
+      if (symbols.isEmpty) {
+        return null;
+      }
+
+      // Ensure symbols are sorted to be able to use binary search.
+      symbols.sort((a, b) => a.addr.compareTo(b.addr));
+
+      // `nm` prints virtual addresses - convert these to file offsets
+      // using loading bias.
+      final fileOffsets = Uint32List(symbols.length);
+      final names = List.generate(symbols.length, (i) => symbols[i].name);
+      for (var i = 0; i < symbols.length; i++) {
+        if (symbols[i].addr < loadingBias) {
+          throw StateError(
+              'unexpected: virtual address ${symbols[i].addr} of symbol '
+              '${symbols[i].name} is less than loading bias $loadingBias');
+        }
+        fileOffsets[i] = symbols[i].addr - loadingBias;
+      }
+
+      return Symbols._(fileOffsets, names);
+    } catch (_) {
+      print('failed to load symbols from $path');
+      return null;
+    }
+  }
+}
diff --git a/runtime/tools/profiling/pubspec.yaml b/runtime/tools/profiling/pubspec.yaml
new file mode 100644
index 0000000..fc0ba9c
--- /dev/null
+++ b/runtime/tools/profiling/pubspec.yaml
@@ -0,0 +1,16 @@
+name: profiling
+description: Utilities for low-level profiling of Dart code
+version: 0.1.0
+publish_to: none
+
+environment:
+  sdk: '>=3.6.0 <4.0.0'
+
+dependencies:
+  protobuf: ^3.1.0
+  fixnum: ^1.1.0
+  path: ^1.9.0
+
+dev_dependencies:
+  lints: ^4.0.0
+  test: ^1.24.0
diff --git a/runtime/tools/run_clang_tidy.dart b/runtime/tools/run_clang_tidy.dart
index 0e0cc94..e0c0634 100644
--- a/runtime/tools/run_clang_tidy.dart
+++ b/runtime/tools/run_clang_tidy.dart
@@ -132,7 +132,7 @@
   'runtime/vm/os_thread_linux.h',
   'runtime/vm/os_thread_macos.h',
   'runtime/vm/os_thread_win.h',
-  'runtime/vm/regexp_assembler_bytecode_inl.h',
+  'runtime/vm/regexp/regexp_assembler_bytecode_inl.h',
   'runtime/vm/simulator_arm.h',
   'runtime/vm/simulator_arm64.h',
   'runtime/vm/simulator_riscv.h',
diff --git a/runtime/vm/BUILD.gn b/runtime/vm/BUILD.gn
index b0b270fc..2825acf 100644
--- a/runtime/vm/BUILD.gn
+++ b/runtime/vm/BUILD.gn
@@ -39,6 +39,7 @@
 import("compiler/compiler_sources.gni")
 import("ffi/ffi_sources.gni")
 import("heap/heap_sources.gni")
+import("regexp/regexp_sources.gni")
 import("vm_sources.gni")
 
 config("libdart_vm_config") {
@@ -193,7 +194,8 @@
   sources = vm_sources + rebase_path(compiler_api_sources, ".", "./compiler/") +
             rebase_path(disassembler_sources, ".", "./compiler/") +
             rebase_path(ffi_sources, ".", "./ffi/") +
-            rebase_path(heap_sources, ".", "./heap/")
+            rebase_path(heap_sources, ".", "./heap/") +
+            rebase_path(regexp_sources, ".", "./regexp/")
   if (is_android) {
     # Android specific workaround for a kernel bug. This source file can't
     # go into vm_sources list because it will break Windows build which
diff --git a/runtime/vm/canonical_tables.cc b/runtime/vm/canonical_tables.cc
index b73c90f..245f025 100644
--- a/runtime/vm/canonical_tables.cc
+++ b/runtime/vm/canonical_tables.cc
@@ -4,7 +4,7 @@
 
 #include "vm/canonical_tables.h"
 
-#include "vm/regexp.h"
+#include "vm/regexp/regexp.h"
 
 namespace dart {
 
diff --git a/runtime/vm/compiler/aot/precompiler.cc b/runtime/vm/compiler/aot/precompiler.cc
index 317ba35..61811ef 100644
--- a/runtime/vm/compiler/aot/precompiler.cc
+++ b/runtime/vm/compiler/aot/precompiler.cc
@@ -47,8 +47,8 @@
 #include "vm/os.h"
 #include "vm/parser.h"
 #include "vm/program_visitor.h"
-#include "vm/regexp_assembler.h"
-#include "vm/regexp_parser.h"
+#include "vm/regexp/regexp_assembler.h"
+#include "vm/regexp/regexp_parser.h"
 #include "vm/resolver.h"
 #include "vm/runtime_entry.h"
 #include "vm/stack_trace.h"
diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc
index 432e63cb..e36dfcd 100644
--- a/runtime/vm/compiler/backend/il.cc
+++ b/runtime/vm/compiler/backend/il.cc
@@ -37,7 +37,7 @@
 #include "vm/object.h"
 #include "vm/object_store.h"
 #include "vm/os.h"
-#include "vm/regexp_assembler_ir.h"
+#include "vm/regexp/regexp_assembler_ir.h"
 #include "vm/resolver.h"
 #include "vm/runtime_entry.h"
 #include "vm/scopes.h"
diff --git a/runtime/vm/compiler/backend/type_propagator.cc b/runtime/vm/compiler/backend/type_propagator.cc
index b1a81b6..903af47 100644
--- a/runtime/vm/compiler/backend/type_propagator.cc
+++ b/runtime/vm/compiler/backend/type_propagator.cc
@@ -9,7 +9,7 @@
 #include "vm/bit_vector.h"
 #include "vm/compiler/compiler_state.h"
 #include "vm/object_store.h"
-#include "vm/regexp_assembler.h"
+#include "vm/regexp/regexp_assembler.h"
 #include "vm/resolver.h"
 #include "vm/timeline.h"
 
diff --git a/runtime/vm/compiler/jit/compiler.cc b/runtime/vm/compiler/jit/compiler.cc
index c08ea0d..a0d07a6 100644
--- a/runtime/vm/compiler/jit/compiler.cc
+++ b/runtime/vm/compiler/jit/compiler.cc
@@ -37,8 +37,8 @@
 #include "vm/object_store.h"
 #include "vm/os.h"
 #include "vm/parser.h"
-#include "vm/regexp_assembler.h"
-#include "vm/regexp_parser.h"
+#include "vm/regexp/regexp_assembler.h"
+#include "vm/regexp/regexp_parser.h"
 #include "vm/runtime_entry.h"
 #include "vm/symbols.h"
 #include "vm/tags.h"
diff --git a/runtime/vm/compiler/stub_code_compiler.cc b/runtime/vm/compiler/stub_code_compiler.cc
index 7677680..7d28e45 100644
--- a/runtime/vm/compiler/stub_code_compiler.cc
+++ b/runtime/vm/compiler/stub_code_compiler.cc
@@ -3314,6 +3314,12 @@
   GenerateSubtypeNTestCacheStub(assembler, 7);
 }
 
+#ifndef DART_TARGET_SUPPORTS_PROBE_POINTS
+void StubCodeCompiler::GenerateAllocationProbePointStub() {
+  __ Stop("allocation probes are not supported on this platform");
+}
+#endif
+
 }  // namespace compiler
 
 }  // namespace dart
diff --git a/runtime/vm/compiler/stub_code_compiler_arm64.cc b/runtime/vm/compiler/stub_code_compiler_arm64.cc
index c607b79..f8ad177 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm64.cc
@@ -28,6 +28,10 @@
 #define __ assembler->
 
 namespace dart {
+#ifdef DART_TARGET_SUPPORTS_PROBE_POINTS
+DECLARE_FLAG(bool, generate_probe_points);
+#endif
+
 namespace compiler {
 
 // Ensures that [R0] is a new object, if not it will be added to the remembered
@@ -1303,6 +1307,46 @@
   GenerateNoSuchMethodDispatcherBody(assembler);
 }
 
+#ifdef DART_TARGET_SUPPORTS_PROBE_POINTS
+void StubCodeCompiler::GenerateAllocationProbePointStub() {
+  if (!FLAG_generate_probe_points) {
+    __ Stop("unexpected invocation of an allocation probe");
+    return;
+  }
+
+  // Create a frame on the stack so that we could properly unwind.
+  // Our .eh_frame is very simple and specifies
+  //       CFA := FP+16; FP := *(FP+0); LR := *(FP+1);
+  // for the whole .text section.
+  __ EnterStubFrame();
+  // Restore native stack pointer (CSP). Dart SP is currently not
+  // the same because we do not follow ABI which requires native SP
+  // to be 16 bytes aligned. Restoring CSP is important for simpleperf
+  // to be able to unwind the stack - as it copies stack range starting
+  // at CSP before unwinding. If CSP is not restored we copy wrong part
+  // of the stack (CSP is bumped almost to the end of the thread stack).
+  __ andi(CSP, SP, Immediate(~15));
+  // Probe will be placed here by `runtime/tools/profiling/bin/set_uprobe.dart`.
+  const intptr_t probe_offset = __ CodeSize();
+  __ SetupCSPFromThread(THR);
+  __ LeaveStubFrame();
+  __ Ret();
+  // This dummy instruction is encoding offset to the probe point. It will be
+  // used by set_uprobe.dart script.
+  __ TestImmediate(R0, probe_offset);
+}
+#endif
+
+static void InvokeAllocationProbePoint(Assembler* assembler) {
+#ifdef DART_TARGET_SUPPORTS_PROBE_POINTS
+  if (FLAG_precompiled_mode && FLAG_generate_probe_points) {
+    __ EnterStubFrame();
+    __ Call(StubCode::AllocationProbePoint());
+    __ LeaveStubFrame();
+  }
+#endif
+}
+
 // Called for inline allocation of arrays.
 // Input registers (preserved):
 //   LR: return address.
@@ -1442,6 +1486,7 @@
     // Done allocating and initializing the array.
     // AllocateArrayABI::kResultReg: new object.
     // AllocateArrayABI::kLengthReg: array length as Smi (preserved).
+    InvokeAllocationProbePoint(assembler);
     __ ret();
 
     // Unable to allocate the array using the fast inline code, just call
@@ -1478,6 +1523,7 @@
     Label slow_case;
     __ TryAllocate(compiler::MintClass(), &slow_case, Assembler::kNearJump,
                    AllocateMintABI::kResultReg, AllocateMintABI::kTempReg);
+    InvokeAllocationProbePoint(assembler);
     __ Ret();
 
     __ Bind(&slow_case);
@@ -1496,6 +1542,7 @@
     Label slow_case;
     __ TryAllocate(compiler::MintClass(), &slow_case, Assembler::kNearJump,
                    AllocateMintABI::kResultReg, AllocateMintABI::kTempReg);
+    InvokeAllocationProbePoint(assembler);
     __ Ret();
 
     __ Bind(&slow_case);
@@ -1923,6 +1970,7 @@
 
     // Done allocating and initializing the context.
     // R0: new object.
+    InvokeAllocationProbePoint(assembler);
     __ ret();
 
     __ Bind(&slow_case);
@@ -1999,6 +2047,7 @@
 
     // Done allocating and initializing the context.
     // R0: new object.
+    InvokeAllocationProbePoint(assembler);
     __ ret();
 
     __ Bind(&slow_case);
@@ -2345,6 +2394,7 @@
       __ Bind(&not_parameterized_case);
     }  // kClsIdReg = R4, kTypeOffsetReg = R5
 
+    InvokeAllocationProbePoint(assembler);
     __ ret();
 
     __ Bind(&slow_case);
@@ -3940,6 +3990,7 @@
     __ b(&loop, UNSIGNED_LESS);
     __ WriteAllocationCanary(R1);  // Fix overshoot.
 
+    InvokeAllocationProbePoint(assembler);
     __ Ret();
 
     __ Bind(&call_runtime);
diff --git a/runtime/vm/compiler/stub_code_compiler_x64.cc b/runtime/vm/compiler/stub_code_compiler_x64.cc
index 021ccc5..f578848 100644
--- a/runtime/vm/compiler/stub_code_compiler_x64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_x64.cc
@@ -31,6 +31,11 @@
 #define __ assembler->
 
 namespace dart {
+
+#ifdef DART_TARGET_SUPPORTS_PROBE_POINTS
+DECLARE_FLAG(bool, generate_probe_points);
+#endif
+
 namespace compiler {
 
 // Ensures that [RAX] is a new object, if not it will be added to the remembered
@@ -1240,6 +1245,34 @@
   GenerateNoSuchMethodDispatcherBody(assembler, /*receiver_reg=*/RDX);
 }
 
+#ifdef DART_TARGET_SUPPORTS_PROBE_POINTS
+void StubCodeCompiler::GenerateAllocationProbePointStub() {
+  if (!FLAG_generate_probe_points) {
+    __ Stop("unexpected invocation of an allocation probe");
+    return;
+  }
+
+  __ EnterStubFrame();
+  // Probe will be placed here by `runtime/tools/profiling/bin/set_uprobe.dart`.
+  const intptr_t probe_offset = __ CodeSize();
+  __ LeaveStubFrame();
+  __ Ret();
+  // This dummy instruction is encoding offset to the probe point. It will be
+  // used by set_uprobe.dart script.
+  __ TestImmediate(RAX, Immediate(probe_offset));
+}
+#endif
+
+static void InvokeAllocationProbePoint(Assembler* assembler) {
+#ifdef DART_TARGET_SUPPORTS_PROBE_POINTS
+  if (FLAG_precompiled_mode && FLAG_generate_probe_points) {
+    __ EnterStubFrame();
+    __ Call(StubCode::AllocationProbePoint());
+    __ LeaveStubFrame();
+  }
+#endif
+}
+
 // Called for inline allocation of arrays.
 // Input registers (preserved):
 //   AllocateArrayABI::kLengthReg: array length as Smi.
@@ -1360,6 +1393,8 @@
     __ cmpq(RDI, RCX);
     __ j(UNSIGNED_LESS, &loop);
     __ WriteAllocationCanary(RCX);
+
+    InvokeAllocationProbePoint(assembler);
     __ ret();
 
     // Unable to allocate the array using the fast inline code, just call
@@ -1393,6 +1428,7 @@
     Label slow_case;
     __ TryAllocate(compiler::MintClass(), &slow_case, Assembler::kNearJump,
                    AllocateMintABI::kResultReg, AllocateMintABI::kTempReg);
+    InvokeAllocationProbePoint(assembler);
     __ Ret();
 
     __ Bind(&slow_case);
@@ -1411,6 +1447,7 @@
     Label slow_case;
     __ TryAllocate(compiler::MintClass(), &slow_case, Assembler::kNearJump,
                    AllocateMintABI::kResultReg, AllocateMintABI::kTempReg);
+    InvokeAllocationProbePoint(assembler);
     __ Ret();
 
     __ Bind(&slow_case);
@@ -1859,6 +1896,7 @@
 
     // Done allocating and initializing the context.
     // RAX: new object.
+    InvokeAllocationProbePoint(assembler);
     __ ret();
 
     __ Bind(&slow_case);
@@ -1879,7 +1917,6 @@
   // RAX: new object
   // Restore the frame pointer.
   __ LeaveStubFrame();
-
   __ ret();
 }
 
@@ -1930,6 +1967,7 @@
 
     // Done allocating and initializing the context.
     // RAX: new object.
+    InvokeAllocationProbePoint(assembler);
     __ ret();
 
     __ Bind(&slow_case);
@@ -1952,7 +1990,6 @@
   // RAX: new object
   // Restore the frame pointer.
   __ LeaveStubFrame();
-
   __ ret();
 }
 
@@ -2266,6 +2303,7 @@
       __ Bind(&not_parameterized_case);
     }  // kTypeOffsetReg = RDI;
 
+    InvokeAllocationProbePoint(assembler);
     __ ret();
 
     __ Bind(&slow_case);
@@ -3869,6 +3907,7 @@
     __ j(UNSIGNED_LESS, &loop, Assembler::kNearJump);
 
     __ WriteAllocationCanary(RCX);  // Fix overshoot.
+    InvokeAllocationProbePoint(assembler);
     __ ret();
 
     __ Bind(&call_runtime);
diff --git a/runtime/vm/debugger.h b/runtime/vm/debugger.h
index 8dc7311..0afda79 100644
--- a/runtime/vm/debugger.h
+++ b/runtime/vm/debugger.h
@@ -884,8 +884,6 @@
   // If any other |ErrorPtr| is returned, it means that a |BreakpointLocation|
   // was not prepared successfully, and the return value will point to an
   // |Error| describing why the |BreakpointLocation| could not be prepared.
-  // TODO(derekxu16): Continue looking at all usages of functions that return
-  // |ErrorPtr|s and account for Object::no_debuggable_code_error().
   ErrorPtr SetBreakpoint(const GrowableHandlePtrArray<const Script>& scripts,
                          TokenPosition token_pos,
                          TokenPosition last_token_pos,
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 5ff31eb..216a77c 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -58,7 +58,7 @@
 #include "vm/os.h"
 #include "vm/parser.h"
 #include "vm/profiler.h"
-#include "vm/regexp.h"
+#include "vm/regexp/regexp.h"
 #include "vm/resolver.h"
 #include "vm/reusable_handles.h"
 #include "vm/reverse_pc_lookup_cache.h"
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
index e1be656..7a8f9f3 100644
--- a/runtime/vm/parser.cc
+++ b/runtime/vm/parser.cc
@@ -28,7 +28,7 @@
 #include "vm/object.h"
 #include "vm/object_store.h"
 #include "vm/os.h"
-#include "vm/regexp_assembler.h"
+#include "vm/regexp/regexp_assembler.h"
 #include "vm/resolver.h"
 #include "vm/scopes.h"
 #include "vm/stack_frame.h"
diff --git a/runtime/vm/regexp.cc b/runtime/vm/regexp/regexp.cc
similarity index 99%
rename from runtime/vm/regexp.cc
rename to runtime/vm/regexp/regexp.cc
index a930e5c..501924b 100644
--- a/runtime/vm/regexp.cc
+++ b/runtime/vm/regexp/regexp.cc
@@ -2,7 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-#include "vm/regexp.h"
+#include "vm/regexp/regexp.h"
 
 #include <memory>
 
@@ -12,15 +12,15 @@
 #include "unicode/uniset.h"
 
 #include "vm/dart_entry.h"
-#include "vm/regexp_assembler.h"
-#include "vm/regexp_assembler_bytecode.h"
-#include "vm/regexp_ast.h"
+#include "vm/regexp/regexp_assembler.h"
+#include "vm/regexp/regexp_assembler_bytecode.h"
+#include "vm/regexp/regexp_ast.h"
+#include "vm/regexp/unibrow-inl.h"
 #include "vm/symbols.h"
 #include "vm/thread.h"
-#include "vm/unibrow-inl.h"
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
-#include "vm/regexp_assembler_ir.h"
+#include "vm/regexp/regexp_assembler_ir.h"
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
 
 #define Z (zone())
diff --git a/runtime/vm/regexp.h b/runtime/vm/regexp/regexp.h
similarity index 99%
rename from runtime/vm/regexp.h
rename to runtime/vm/regexp/regexp.h
index 8657ec7..3675b9e 100644
--- a/runtime/vm/regexp.h
+++ b/runtime/vm/regexp/regexp.h
@@ -2,13 +2,13 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-#ifndef RUNTIME_VM_REGEXP_H_
-#define RUNTIME_VM_REGEXP_H_
+#ifndef RUNTIME_VM_REGEXP_REGEXP_H_
+#define RUNTIME_VM_REGEXP_REGEXP_H_
 
 #include "platform/unicode.h"
 
 #include "vm/object.h"
-#include "vm/regexp_assembler.h"
+#include "vm/regexp/regexp_assembler.h"
 #include "vm/splay-tree.h"
 
 namespace dart {
@@ -1527,4 +1527,4 @@
 
 }  // namespace dart
 
-#endif  // RUNTIME_VM_REGEXP_H_
+#endif  // RUNTIME_VM_REGEXP_REGEXP_H_
diff --git a/runtime/vm/regexp_assembler.cc b/runtime/vm/regexp/regexp_assembler.cc
similarity index 97%
rename from runtime/vm/regexp_assembler.cc
rename to runtime/vm/regexp/regexp_assembler.cc
index 0f1ad62..0fac2fb 100644
--- a/runtime/vm/regexp_assembler.cc
+++ b/runtime/vm/regexp/regexp_assembler.cc
@@ -2,16 +2,16 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-#include "vm/regexp_assembler.h"
+#include "vm/regexp/regexp_assembler.h"
 
 #include "unicode/uchar.h"
 
 #include "platform/unicode.h"
 
 #include "vm/flags.h"
-#include "vm/regexp.h"
+#include "vm/regexp/regexp.h"
+#include "vm/regexp/unibrow-inl.h"
 #include "vm/runtime_entry.h"
-#include "vm/unibrow-inl.h"
 
 namespace dart {
 
diff --git a/runtime/vm/regexp_assembler.h b/runtime/vm/regexp/regexp_assembler.h
similarity index 98%
rename from runtime/vm/regexp_assembler.h
rename to runtime/vm/regexp/regexp_assembler.h
index d2482c2..59cfba3 100644
--- a/runtime/vm/regexp_assembler.h
+++ b/runtime/vm/regexp/regexp_assembler.h
@@ -2,8 +2,8 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-#ifndef RUNTIME_VM_REGEXP_ASSEMBLER_H_
-#define RUNTIME_VM_REGEXP_ASSEMBLER_H_
+#ifndef RUNTIME_VM_REGEXP_REGEXP_ASSEMBLER_H_
+#define RUNTIME_VM_REGEXP_REGEXP_ASSEMBLER_H_
 
 #include "vm/object.h"
 
@@ -266,4 +266,4 @@
 
 }  // namespace dart
 
-#endif  // RUNTIME_VM_REGEXP_ASSEMBLER_H_
+#endif  // RUNTIME_VM_REGEXP_REGEXP_ASSEMBLER_H_
diff --git a/runtime/vm/regexp_assembler_bytecode.cc b/runtime/vm/regexp/regexp_assembler_bytecode.cc
similarity index 97%
rename from runtime/vm/regexp_assembler_bytecode.cc
rename to runtime/vm/regexp/regexp_assembler_bytecode.cc
index ac37a0a..927bba9 100644
--- a/runtime/vm/regexp_assembler_bytecode.cc
+++ b/runtime/vm/regexp/regexp_assembler_bytecode.cc
@@ -2,16 +2,16 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-#include "vm/regexp_assembler_bytecode.h"
+#include "vm/regexp/regexp_assembler_bytecode.h"
 
 #include "vm/exceptions.h"
 #include "vm/object_store.h"
-#include "vm/regexp.h"
-#include "vm/regexp_assembler.h"
-#include "vm/regexp_assembler_bytecode_inl.h"
-#include "vm/regexp_bytecodes.h"
-#include "vm/regexp_interpreter.h"
-#include "vm/regexp_parser.h"
+#include "vm/regexp/regexp.h"
+#include "vm/regexp/regexp_assembler.h"
+#include "vm/regexp/regexp_assembler_bytecode_inl.h"
+#include "vm/regexp/regexp_bytecodes.h"
+#include "vm/regexp/regexp_interpreter.h"
+#include "vm/regexp/regexp_parser.h"
 #include "vm/timeline.h"
 
 namespace dart {
diff --git a/runtime/vm/regexp_assembler_bytecode.h b/runtime/vm/regexp/regexp_assembler_bytecode.h
similarity index 96%
rename from runtime/vm/regexp_assembler_bytecode.h
rename to runtime/vm/regexp/regexp_assembler_bytecode.h
index c8c87af..bbe18ebf 100644
--- a/runtime/vm/regexp_assembler_bytecode.h
+++ b/runtime/vm/regexp/regexp_assembler_bytecode.h
@@ -2,11 +2,11 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-#ifndef RUNTIME_VM_REGEXP_ASSEMBLER_BYTECODE_H_
-#define RUNTIME_VM_REGEXP_ASSEMBLER_BYTECODE_H_
+#ifndef RUNTIME_VM_REGEXP_REGEXP_ASSEMBLER_BYTECODE_H_
+#define RUNTIME_VM_REGEXP_REGEXP_ASSEMBLER_BYTECODE_H_
 
 #include "vm/object.h"
-#include "vm/regexp_assembler.h"
+#include "vm/regexp/regexp_assembler.h"
 
 namespace dart {
 
@@ -143,4 +143,4 @@
 
 }  // namespace dart
 
-#endif  // RUNTIME_VM_REGEXP_ASSEMBLER_BYTECODE_H_
+#endif  // RUNTIME_VM_REGEXP_REGEXP_ASSEMBLER_BYTECODE_H_
diff --git a/runtime/vm/regexp_assembler_bytecode_inl.h b/runtime/vm/regexp/regexp_assembler_bytecode_inl.h
similarity index 85%
rename from runtime/vm/regexp_assembler_bytecode_inl.h
rename to runtime/vm/regexp/regexp_assembler_bytecode_inl.h
index 76e1dc16..a05c788 100644
--- a/runtime/vm/regexp_assembler_bytecode_inl.h
+++ b/runtime/vm/regexp/regexp_assembler_bytecode_inl.h
@@ -4,10 +4,10 @@
 
 // A light-weight assembler for the Irregexp byte code.
 
-#include "vm/regexp_bytecodes.h"
+#include "vm/regexp/regexp_bytecodes.h"
 
-#ifndef RUNTIME_VM_REGEXP_ASSEMBLER_BYTECODE_INL_H_
-#define RUNTIME_VM_REGEXP_ASSEMBLER_BYTECODE_INL_H_
+#ifndef RUNTIME_VM_REGEXP_REGEXP_ASSEMBLER_BYTECODE_INL_H_
+#define RUNTIME_VM_REGEXP_REGEXP_ASSEMBLER_BYTECODE_INL_H_
 
 namespace dart {
 
@@ -51,4 +51,4 @@
 
 }  // namespace dart
 
-#endif  // RUNTIME_VM_REGEXP_ASSEMBLER_BYTECODE_INL_H_
+#endif  // RUNTIME_VM_REGEXP_REGEXP_ASSEMBLER_BYTECODE_INL_H_
diff --git a/runtime/vm/regexp_assembler_ir.cc b/runtime/vm/regexp/regexp_assembler_ir.cc
similarity index 99%
rename from runtime/vm/regexp_assembler_ir.cc
rename to runtime/vm/regexp/regexp_assembler_ir.cc
index eee8061..8a65995 100644
--- a/runtime/vm/regexp_assembler_ir.cc
+++ b/runtime/vm/regexp/regexp_assembler_ir.cc
@@ -4,7 +4,7 @@
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
 
-#include "vm/regexp_assembler_ir.h"
+#include "vm/regexp/regexp_assembler_ir.h"
 
 #include <utility>
 
@@ -17,7 +17,7 @@
 #include "vm/dart_entry.h"
 #include "vm/longjump.h"
 #include "vm/object_store.h"
-#include "vm/regexp.h"
+#include "vm/regexp/regexp.h"
 #include "vm/resolver.h"
 #include "vm/runtime_entry.h"
 #include "vm/stack_frame.h"
diff --git a/runtime/vm/regexp_assembler_ir.h b/runtime/vm/regexp/regexp_assembler_ir.h
similarity index 98%
rename from runtime/vm/regexp_assembler_ir.h
rename to runtime/vm/regexp/regexp_assembler_ir.h
index b65dba2..0f6eee5 100644
--- a/runtime/vm/regexp_assembler_ir.h
+++ b/runtime/vm/regexp/regexp_assembler_ir.h
@@ -2,13 +2,13 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-#ifndef RUNTIME_VM_REGEXP_ASSEMBLER_IR_H_
-#define RUNTIME_VM_REGEXP_ASSEMBLER_IR_H_
+#ifndef RUNTIME_VM_REGEXP_REGEXP_ASSEMBLER_IR_H_
+#define RUNTIME_VM_REGEXP_REGEXP_ASSEMBLER_IR_H_
 
 #include "vm/compiler/assembler/assembler.h"
 #include "vm/compiler/backend/il.h"
 #include "vm/object.h"
-#include "vm/regexp_assembler.h"
+#include "vm/regexp/regexp_assembler.h"
 
 namespace dart {
 
@@ -445,4 +445,4 @@
 
 }  // namespace dart
 
-#endif  // RUNTIME_VM_REGEXP_ASSEMBLER_IR_H_
+#endif  // RUNTIME_VM_REGEXP_REGEXP_ASSEMBLER_IR_H_
diff --git a/runtime/vm/regexp_ast.cc b/runtime/vm/regexp/regexp_ast.cc
similarity index 99%
rename from runtime/vm/regexp_ast.cc
rename to runtime/vm/regexp/regexp_ast.cc
index ad0c300..3bfd878 100644
--- a/runtime/vm/regexp_ast.cc
+++ b/runtime/vm/regexp/regexp_ast.cc
@@ -2,7 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-#include "vm/regexp_ast.h"
+#include "vm/regexp/regexp_ast.h"
 
 #include "platform/utils.h"
 #include "vm/os.h"
diff --git a/runtime/vm/regexp_ast.h b/runtime/vm/regexp/regexp_ast.h
similarity index 98%
rename from runtime/vm/regexp_ast.h
rename to runtime/vm/regexp/regexp_ast.h
index b040d50..2bee2f0 100644
--- a/runtime/vm/regexp_ast.h
+++ b/runtime/vm/regexp/regexp_ast.h
@@ -2,13 +2,13 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-#ifndef RUNTIME_VM_REGEXP_AST_H_
-#define RUNTIME_VM_REGEXP_AST_H_
+#ifndef RUNTIME_VM_REGEXP_REGEXP_AST_H_
+#define RUNTIME_VM_REGEXP_REGEXP_AST_H_
 
 #include "platform/globals.h"
 #include "platform/utils.h"
 #include "vm/allocation.h"
-#include "vm/regexp.h"
+#include "vm/regexp/regexp.h"
 
 namespace dart {
 
@@ -445,4 +445,4 @@
 
 }  // namespace dart
 
-#endif  // RUNTIME_VM_REGEXP_AST_H_
+#endif  // RUNTIME_VM_REGEXP_REGEXP_AST_H_
diff --git a/runtime/vm/regexp_bytecodes.h b/runtime/vm/regexp/regexp_bytecodes.h
similarity index 97%
rename from runtime/vm/regexp_bytecodes.h
rename to runtime/vm/regexp/regexp_bytecodes.h
index e85a335..271a859 100644
--- a/runtime/vm/regexp_bytecodes.h
+++ b/runtime/vm/regexp/regexp_bytecodes.h
@@ -2,8 +2,8 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-#ifndef RUNTIME_VM_REGEXP_BYTECODES_H_
-#define RUNTIME_VM_REGEXP_BYTECODES_H_
+#ifndef RUNTIME_VM_REGEXP_REGEXP_BYTECODES_H_
+#define RUNTIME_VM_REGEXP_REGEXP_BYTECODES_H_
 
 namespace dart {
 
@@ -83,4 +83,4 @@
 
 }  // namespace dart
 
-#endif  // RUNTIME_VM_REGEXP_BYTECODES_H_
+#endif  // RUNTIME_VM_REGEXP_REGEXP_BYTECODES_H_
diff --git a/runtime/vm/regexp_interpreter.cc b/runtime/vm/regexp/regexp_interpreter.cc
similarity index 98%
rename from runtime/vm/regexp_interpreter.cc
rename to runtime/vm/regexp/regexp_interpreter.cc
index dd56fe8..11f6f59 100644
--- a/runtime/vm/regexp_interpreter.cc
+++ b/runtime/vm/regexp/regexp_interpreter.cc
@@ -7,15 +7,15 @@
 #include <memory>
 #include <utility>
 
-#include "heap/safepoint.h"
-#include "vm/regexp_interpreter.h"
+#include "vm/heap/safepoint.h"
+#include "vm/regexp/regexp_interpreter.h"
 
 #include "platform/unicode.h"
 #include "vm/object.h"
-#include "vm/regexp_assembler.h"
-#include "vm/regexp_bytecodes.h"
-#include "vm/unibrow-inl.h"
-#include "vm/unibrow.h"
+#include "vm/regexp/regexp_assembler.h"
+#include "vm/regexp/regexp_bytecodes.h"
+#include "vm/regexp/unibrow-inl.h"
+#include "vm/regexp/unibrow.h"
 
 namespace dart {
 
diff --git a/runtime/vm/regexp_interpreter.h b/runtime/vm/regexp/regexp_interpreter.h
similarity index 84%
rename from runtime/vm/regexp_interpreter.h
rename to runtime/vm/regexp/regexp_interpreter.h
index f81ea43..78c7b56 100644
--- a/runtime/vm/regexp_interpreter.h
+++ b/runtime/vm/regexp/regexp_interpreter.h
@@ -4,8 +4,8 @@
 
 // A simple interpreter for the Irregexp byte code.
 
-#ifndef RUNTIME_VM_REGEXP_INTERPRETER_H_
-#define RUNTIME_VM_REGEXP_INTERPRETER_H_
+#ifndef RUNTIME_VM_REGEXP_REGEXP_INTERPRETER_H_
+#define RUNTIME_VM_REGEXP_REGEXP_INTERPRETER_H_
 
 #include "vm/allocation.h"
 #include "vm/object.h"
@@ -26,4 +26,4 @@
 
 }  // namespace dart
 
-#endif  // RUNTIME_VM_REGEXP_INTERPRETER_H_
+#endif  // RUNTIME_VM_REGEXP_REGEXP_INTERPRETER_H_
diff --git a/runtime/vm/regexp_parser.cc b/runtime/vm/regexp/regexp_parser.cc
similarity index 99%
rename from runtime/vm/regexp_parser.cc
rename to runtime/vm/regexp/regexp_parser.cc
index aaf23b0..792a1b3 100644
--- a/runtime/vm/regexp_parser.cc
+++ b/runtime/vm/regexp/regexp_parser.cc
@@ -2,7 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-#include "vm/regexp_parser.h"
+#include "vm/regexp/regexp_parser.h"
 
 #include "unicode/uchar.h"
 #include "unicode/uniset.h"
diff --git a/runtime/vm/regexp_parser.h b/runtime/vm/regexp/regexp_parser.h
similarity index 98%
rename from runtime/vm/regexp_parser.h
rename to runtime/vm/regexp/regexp_parser.h
index c070d7e..72c3f2f 100644
--- a/runtime/vm/regexp_parser.h
+++ b/runtime/vm/regexp/regexp_parser.h
@@ -2,12 +2,12 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-#ifndef RUNTIME_VM_REGEXP_PARSER_H_
-#define RUNTIME_VM_REGEXP_PARSER_H_
+#ifndef RUNTIME_VM_REGEXP_REGEXP_PARSER_H_
+#define RUNTIME_VM_REGEXP_REGEXP_PARSER_H_
 
 #include "vm/allocation.h"
 #include "vm/growable_array.h"
-#include "vm/regexp_ast.h"
+#include "vm/regexp/regexp_ast.h"
 
 namespace dart {
 
@@ -264,4 +264,4 @@
 
 }  // namespace dart
 
-#endif  // RUNTIME_VM_REGEXP_PARSER_H_
+#endif  // RUNTIME_VM_REGEXP_REGEXP_PARSER_H_
diff --git a/runtime/vm/regexp/regexp_sources.gni b/runtime/vm/regexp/regexp_sources.gni
new file mode 100644
index 0000000..04e640c
--- /dev/null
+++ b/runtime/vm/regexp/regexp_sources.gni
@@ -0,0 +1,27 @@
+# Copyright (c) 2024, the Dart project authors.  Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+regexp_sources = [
+  "regexp.cc",
+  "regexp.h",
+  "regexp_assembler.cc",
+  "regexp_assembler.h",
+  "regexp_assembler_bytecode.cc",
+  "regexp_assembler_bytecode.h",
+  "regexp_assembler_bytecode_inl.h",
+  "regexp_assembler_ir.cc",
+  "regexp_assembler_ir.h",
+  "regexp_ast.cc",
+  "regexp_ast.h",
+  "regexp_bytecodes.h",
+  "regexp_interpreter.cc",
+  "regexp_interpreter.h",
+  "regexp_parser.cc",
+  "regexp_parser.h",
+  "unibrow-inl.h",
+  "unibrow.cc",
+  "unibrow.h",
+]
+
+regexp_sources_tests = [ "regexp_test.cc" ]
diff --git a/runtime/vm/regexp_test.cc b/runtime/vm/regexp/regexp_test.cc
similarity index 96%
rename from runtime/vm/regexp_test.cc
rename to runtime/vm/regexp/regexp_test.cc
index 1a252ec..3f92ccd 100644
--- a/runtime/vm/regexp_test.cc
+++ b/runtime/vm/regexp/regexp_test.cc
@@ -6,8 +6,8 @@
 
 #include "vm/isolate.h"
 #include "vm/object.h"
-#include "vm/regexp.h"
-#include "vm/regexp_assembler_ir.h"
+#include "vm/regexp/regexp.h"
+#include "vm/regexp/regexp_assembler_ir.h"
 #include "vm/unit_test.h"
 
 namespace dart {
diff --git a/runtime/vm/unibrow-inl.h b/runtime/vm/regexp/unibrow-inl.h
similarity index 87%
rename from runtime/vm/unibrow-inl.h
rename to runtime/vm/regexp/unibrow-inl.h
index ab6f5d5..a8f1037 100644
--- a/runtime/vm/unibrow-inl.h
+++ b/runtime/vm/regexp/unibrow-inl.h
@@ -2,10 +2,10 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-#ifndef RUNTIME_VM_UNIBROW_INL_H_
-#define RUNTIME_VM_UNIBROW_INL_H_
+#ifndef RUNTIME_VM_REGEXP_UNIBROW_INL_H_
+#define RUNTIME_VM_REGEXP_UNIBROW_INL_H_
 
-#include "vm/unibrow.h"
+#include "vm/regexp/unibrow.h"
 
 #include "platform/assert.h"
 
@@ -45,4 +45,4 @@
 
 }  // namespace unibrow
 
-#endif  // RUNTIME_VM_UNIBROW_INL_H_
+#endif  // RUNTIME_VM_REGEXP_UNIBROW_INL_H_
diff --git a/runtime/vm/unibrow.cc b/runtime/vm/regexp/unibrow.cc
similarity index 99%
rename from runtime/vm/unibrow.cc
rename to runtime/vm/regexp/unibrow.cc
index 4f468ee..affa699 100644
--- a/runtime/vm/unibrow.cc
+++ b/runtime/vm/regexp/unibrow.cc
@@ -4,8 +4,8 @@
 //
 // This file was generated at 2014-10-08 15:25:47.940335 (in v8, copied to dart)
 
-#include "vm/unibrow.h"
-#include "vm/unibrow-inl.h"
+#include "vm/regexp/unibrow.h"
+#include "vm/regexp/unibrow-inl.h"
 
 #include <stdio.h>
 #include <stdlib.h>
diff --git a/runtime/vm/unibrow.h b/runtime/vm/regexp/unibrow.h
similarity index 94%
rename from runtime/vm/unibrow.h
rename to runtime/vm/regexp/unibrow.h
index c01c2aa..3caada9 100644
--- a/runtime/vm/unibrow.h
+++ b/runtime/vm/regexp/unibrow.h
@@ -2,8 +2,8 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-#ifndef RUNTIME_VM_UNIBROW_H_
-#define RUNTIME_VM_UNIBROW_H_
+#ifndef RUNTIME_VM_REGEXP_UNIBROW_H_
+#define RUNTIME_VM_REGEXP_UNIBROW_H_
 
 #include <sys/types.h>
 
@@ -69,4 +69,4 @@
 
 }  // namespace unibrow
 
-#endif  // RUNTIME_VM_UNIBROW_H_
+#endif  // RUNTIME_VM_REGEXP_UNIBROW_H_
diff --git a/runtime/vm/stub_code.cc b/runtime/vm/stub_code.cc
index 7b7216c..e2699a8 100644
--- a/runtime/vm/stub_code.cc
+++ b/runtime/vm/stub_code.cc
@@ -24,6 +24,13 @@
 
 DECLARE_FLAG(bool, precompiled_mode);
 
+#ifdef DART_TARGET_SUPPORTS_PROBE_POINTS
+DEFINE_FLAG(bool,
+            generate_probe_points,
+            false,
+            "Generate probe points for installation of user space probes");
+#endif
+
 StubCode::StubCodeEntry StubCode::entries_[kNumStubEntries] = {
 #if defined(DART_PRECOMPILED_RUNTIME)
 #define STUB_CODE_DECLARE(name) {nullptr, #name},
diff --git a/runtime/vm/stub_code_list.h b/runtime/vm/stub_code_list.h
index 2d29e52..7c14e75 100644
--- a/runtime/vm/stub_code_list.h
+++ b/runtime/vm/stub_code_list.h
@@ -18,6 +18,14 @@
   V(LazySpecializeTypeTest)                                                    \
   V(LazySpecializeNullableTypeTest)
 
+#if (defined(DART_TARGET_OS_LINUX) || defined(DART_TARGET_OS_ANDROID)) &&      \
+    (defined(TARGET_ARCH_X64) || defined(TARGET_ARCH_ARM64))
+// Currently we support probe points only Linux and Android (X64 and ARM64).
+#define DART_TARGET_SUPPORTS_PROBE_POINTS 1
+#endif
+
+#define PROBE_POINT_STUBS_LIST(V) V(AllocationProbePoint)
+
 // List of stubs created in the VM isolate, these stubs are shared by different
 // isolates running in this dart process.
 #define VM_STUB_CODE_LIST(V)                                                   \
@@ -28,6 +36,7 @@
   V(WriteBarrier)                                                              \
   V(WriteBarrierWrappers)                                                      \
   V(ArrayWriteBarrier)                                                         \
+  PROBE_POINT_STUBS_LIST(V)                                                    \
   V(AllocateArray)                                                             \
   V(AllocateMint)                                                              \
   V(AllocateDouble)                                                            \
diff --git a/runtime/vm/vm_sources.gni b/runtime/vm/vm_sources.gni
index 4a42526..bc62caf 100644
--- a/runtime/vm/vm_sources.gni
+++ b/runtime/vm/vm_sources.gni
@@ -252,22 +252,6 @@
   "raw_object.h",
   "raw_object_fields.cc",
   "raw_object_fields.h",
-  "regexp.cc",
-  "regexp.h",
-  "regexp_assembler.cc",
-  "regexp_assembler.h",
-  "regexp_assembler_bytecode.cc",
-  "regexp_assembler_bytecode.h",
-  "regexp_assembler_bytecode_inl.h",
-  "regexp_assembler_ir.cc",
-  "regexp_assembler_ir.h",
-  "regexp_ast.cc",
-  "regexp_ast.h",
-  "regexp_bytecodes.h",
-  "regexp_interpreter.cc",
-  "regexp_interpreter.h",
-  "regexp_parser.cc",
-  "regexp_parser.h",
   "report.cc",
   "report.h",
   "resolver.cc",
@@ -361,9 +345,6 @@
   "token_position.h",
   "type_testing_stubs.cc",
   "type_testing_stubs.h",
-  "unibrow-inl.h",
-  "unibrow.cc",
-  "unibrow.h",
   "unicode.cc",
   "unicode_data.cc",
   "unwinding_records.cc",
@@ -454,7 +435,6 @@
   "os_test.cc",
   "port_test.cc",
   "profiler_test.cc",
-  "regexp_test.cc",
   "ring_buffer_test.cc",
   "scopes_test.cc",
   "service_test.cc",
diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn
index e03cf3e..93cf022 100644
--- a/sdk/BUILD.gn
+++ b/sdk/BUILD.gn
@@ -43,7 +43,8 @@
 # ......snapshots/
 # ........analysis_server.dart.snapshot
 # ........dart2bytecode.snapshot (if dart_dynamic_modules)
-# ........dart2js.dart.snapshot
+# ........dart2js_aot.dart.snapshot (AOT snapshot, if not on ia32)
+# ........dart2js.dart.snapshot (JIT snapshot only on ia32)
 # ........dart2wasm_product.snapshot (if not on ia32)
 # ........dartdev.dart.snapshot (app-jit snapshot or kernel dill file)
 # ........dartdevc.dart.snapshot
@@ -153,10 +154,6 @@
 
 _full_sdk_snapshots = _platform_sdk_snapshots + [
                         [
-                          "dart2js",
-                          "../utils/compiler:dart2js",
-                        ],
-                        [
                           "dartdevc",
                           "../utils/ddc:dartdevc",
                         ],
@@ -165,6 +162,17 @@
                           "../utils/bazel:kernel_worker",
                         ],
                       ]
+if (dart_target_arch != "ia32" && dart_target_arch != "x86") {
+  _full_sdk_snapshots += [ [
+        "dart2js_aot",
+        "../utils/compiler:dart2js_sdk_aot",
+      ] ]
+} else {
+  _full_sdk_snapshots += [ [
+        "dart2js",
+        "../utils/compiler:dart2js",
+      ] ]
+}
 
 # Libraries that go under lib/
 _full_sdk_libraries = [
diff --git a/sdk/lib/core/duration.dart b/sdk/lib/core/duration.dart
index 64a7874..4f62fc8 100644
--- a/sdk/lib/core/duration.dart
+++ b/sdk/lib/core/duration.dart
@@ -34,12 +34,12 @@
 /// as "hours" to the constructor, and can be larger than 59.
 ///
 /// ```dart
-/// const fastestMarathon = Duration(hours: 2, minutes: 3, seconds: 2);
+/// const fastestMarathon = Duration(hours: 2, minutes: 0, seconds: 35);
 /// print(fastestMarathon.inDays); // 0
 /// print(fastestMarathon.inHours); // 2
-/// print(fastestMarathon.inMinutes); // 123
-/// print(fastestMarathon.inSeconds); // 7382
-/// print(fastestMarathon.inMilliseconds); // 7382000
+/// print(fastestMarathon.inMinutes); // 120
+/// print(fastestMarathon.inSeconds); // 7235
+/// print(fastestMarathon.inMilliseconds); // 7235000
 /// ```
 /// The duration can be negative, in which case
 /// all the properties derived from the duration are also non-positive.
diff --git a/tests/web/wasm/multi_module_stress_test.dart b/tests/web/wasm/multi_module_stress_test.dart
index 89cf936..fef42d4 100644
--- a/tests/web/wasm/multi_module_stress_test.dart
+++ b/tests/web/wasm/multi_module_stress_test.dart
@@ -2,7 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// dart2wasmOptions=--extra-compiler-option=--enable-multi-module-stress-test-mode
+// dart2wasmOptions=--extra-compiler-option=--enable-multi-module-stress-test-mode --extra-compiler-option=--no-js-compatibility
 
 import 'package:async_helper/async_helper.dart';
 import 'package:compiler/src/util/memory_compiler.dart' as dart2js;
diff --git a/tools/VERSION b/tools/VERSION
index 7caf555..eda9e50 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -25,7 +25,7 @@
 #
 CHANNEL dev
 MAJOR 3
-MINOR 6
+MINOR 7
 PATCH 0
-PRERELEASE 334
+PRERELEASE 0
 PRERELEASE_PATCH 0
diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json
index 38757db..b1f1013 100644
--- a/tools/bots/test_matrix.json
+++ b/tools/bots/test_matrix.json
@@ -2865,7 +2865,8 @@
           "script": "tools/build.py",
           "arguments": [
             "runtime",
-            "dart2wasm"
+            "dart2wasm",
+            "create_sdk"
           ]
         },
         {
diff --git a/tools/experimental_features.yaml b/tools/experimental_features.yaml
index 8cca7fb..87f550d 100644
--- a/tools/experimental_features.yaml
+++ b/tools/experimental_features.yaml
@@ -109,7 +109,7 @@
 # default 'language' "category" with code generated for both CFE and Analyzer,
 # while other categories can be tailored more specifically.
 
-current-version: '3.6.0'
+current-version: '3.7.0'
 
 features:
   variance:
diff --git a/utils/compiler/BUILD.gn b/utils/compiler/BUILD.gn
index 7a3093b..f76196a 100644
--- a/utils/compiler/BUILD.gn
+++ b/utils/compiler/BUILD.gn
@@ -87,6 +87,22 @@
   name = "dart2js_aot"
 }
 
+aot_snapshot("dart2js_sdk_aot") {
+  deps = [ ":dart2js_create_snapshot_entry" ]
+
+  main_dart = "$target_gen_dir/dart2js.dart"
+  name = "dart2js_aot.dart"
+  output = "$root_gen_dir/dart2js_aot.dart.snapshot"
+
+  # dartaotruntime has dart_product_config applied to it,
+  # so it is built in # product mode in both release and
+  # product builds, and is only built in debug mode in debug
+  # builds. The following line ensures that the dartaotruntime
+  # and dartdevc_aot snapshot in an SDK build are
+  # always compatible with each other.
+  force_product_mode = !dart_debug
+}
+
 compile_platform("compile_dart2js_platform_unsound") {
   single_root_scheme = "org-dartlang-sdk"
   single_root_base = rebase_path("$sdk_root/")