Merge "perfetto: switch base::Optional to std::optional"
diff --git a/BUILD b/BUILD
index cb6b454..8cc1999 100644
--- a/BUILD
+++ b/BUILD
@@ -408,7 +408,6 @@
         "include/perfetto/ext/base/metatrace.h",
         "include/perfetto/ext/base/metatrace_events.h",
         "include/perfetto/ext/base/no_destructor.h",
-        "include/perfetto/ext/base/optional.h",
         "include/perfetto/ext/base/paged_memory.h",
         "include/perfetto/ext/base/periodic_task.h",
         "include/perfetto/ext/base/pipe.h",
diff --git a/include/perfetto/ext/base/BUILD.gn b/include/perfetto/ext/base/BUILD.gn
index 5a44be4..6e38737 100644
--- a/include/perfetto/ext/base/BUILD.gn
+++ b/include/perfetto/ext/base/BUILD.gn
@@ -32,7 +32,6 @@
     "metatrace.h",
     "metatrace_events.h",
     "no_destructor.h",
-    "optional.h",
     "paged_memory.h",
     "periodic_task.h",
     "pipe.h",
diff --git a/include/perfetto/ext/base/base64.h b/include/perfetto/ext/base/base64.h
index 030a83d..5a01164 100644
--- a/include/perfetto/ext/base/base64.h
+++ b/include/perfetto/ext/base/base64.h
@@ -17,9 +17,9 @@
 #ifndef INCLUDE_PERFETTO_EXT_BASE_BASE64_H_
 #define INCLUDE_PERFETTO_EXT_BASE_BASE64_H_
 
+#include <optional>
 #include <string>
 
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/ext/base/utils.h"  // For ssize_t.
 
@@ -56,9 +56,9 @@
                      uint8_t* dst,
                      size_t dst_size);
 
-Optional<std::string> Base64Decode(const char* src, size_t src_size);
+std::optional<std::string> Base64Decode(const char* src, size_t src_size);
 
-inline Optional<std::string> Base64Decode(StringView sv) {
+inline std::optional<std::string> Base64Decode(StringView sv) {
   return Base64Decode(sv.data(), sv.size());
 }
 
diff --git a/include/perfetto/ext/base/http/http_server.h b/include/perfetto/ext/base/http/http_server.h
index c251061..2fe563b 100644
--- a/include/perfetto/ext/base/http/http_server.h
+++ b/include/perfetto/ext/base/http/http_server.h
@@ -21,10 +21,10 @@
 #include <initializer_list>
 #include <list>
 #include <memory>
+#include <optional>
 #include <string>
 
 #include "perfetto/base/task_runner.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/paged_memory.h"
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/ext/base/unix_socket.h"
@@ -37,7 +37,7 @@
 struct HttpRequest {
   explicit HttpRequest(HttpServerConnection* c) : conn(c) {}
 
-  Optional<StringView> GetHeader(StringView name) const;
+  std::optional<StringView> GetHeader(StringView name) const;
 
   HttpServerConnection* conn;
 
diff --git a/include/perfetto/ext/base/optional.h b/include/perfetto/ext/base/optional.h
deleted file mode 100644
index 4ea781f..0000000
--- a/include/perfetto/ext/base/optional.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef INCLUDE_PERFETTO_EXT_BASE_OPTIONAL_H_
-#define INCLUDE_PERFETTO_EXT_BASE_OPTIONAL_H_
-
-#include <functional>
-#include <optional>
-
-namespace perfetto {
-namespace base {
-
-template <typename T>
-using Optional = std::optional<T>;
-
-inline constexpr std::nullopt_t nullopt = std::nullopt;
-
-template <class T>
-constexpr std::optional<std::decay_t<T>> make_optional(T&& value) {
-  return std::make_optional<T>(std::forward<T>(value));
-}
-
-template <class T, class... Args>
-constexpr std::optional<T> make_optional(Args&&... args) {
-  return std::make_optional<T>(std::forward<Args...>(args)...);
-}
-
-}  // namespace base
-}  // namespace perfetto
-
-#endif  // INCLUDE_PERFETTO_EXT_BASE_OPTIONAL_H_
diff --git a/include/perfetto/ext/base/status_or.h b/include/perfetto/ext/base/status_or.h
index 7067295..46387d7 100644
--- a/include/perfetto/ext/base/status_or.h
+++ b/include/perfetto/ext/base/status_or.h
@@ -17,8 +17,9 @@
 #ifndef INCLUDE_PERFETTO_EXT_BASE_STATUS_OR_H_
 #define INCLUDE_PERFETTO_EXT_BASE_STATUS_OR_H_
 
+#include <optional>
+
 #include "perfetto/base/status.h"
-#include "perfetto/ext/base/optional.h"
 
 namespace perfetto {
 namespace base {
@@ -38,7 +39,7 @@
 
   // Intentionally implicit to allow idomatic usage (e.g. returning value/status
   // from base::StatusOr returning function).
-  StatusOr(base::Status status) : StatusOr(std::move(status), base::nullopt) {
+  StatusOr(base::Status status) : StatusOr(std::move(status), std::nullopt) {
     if (status.ok()) {
       // Matches what Abseil's approach towards OkStatus being passed to
       // absl::StatusOr<T>.
@@ -63,13 +64,13 @@
   const T* operator->() const { return &value(); }
 
  private:
-  StatusOr(base::Status status, base::Optional<T> value)
+  StatusOr(base::Status status, std::optional<T> value)
       : status_(std::move(status)), value_(std::move(value)) {
     PERFETTO_DCHECK(!status_.ok() || value_.has_value());
   }
 
   base::Status status_;
-  base::Optional<T> value_;
+  std::optional<T> value_;
 };
 
 }  // namespace base
diff --git a/include/perfetto/ext/base/string_utils.h b/include/perfetto/ext/base/string_utils.h
index 828d214..20e5799 100644
--- a/include/perfetto/ext/base/string_utils.h
+++ b/include/perfetto/ext/base/string_utils.h
@@ -22,10 +22,10 @@
 #include <string.h>
 
 #include <cinttypes>
+#include <optional>
 #include <string>
 #include <vector>
 
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_view.h"
 
 namespace perfetto {
@@ -39,59 +39,63 @@
   return ('a' <= c && c <= 'z') ? static_cast<char>(c + ('A' - 'a')) : c;
 }
 
-inline Optional<uint32_t> CStringToUInt32(const char* s, int base = 10) {
+inline std::optional<uint32_t> CStringToUInt32(const char* s, int base = 10) {
   char* endptr = nullptr;
   auto value = static_cast<uint32_t>(strtoul(s, &endptr, base));
-  return (*s && !*endptr) ? base::make_optional(value) : base::nullopt;
+  return (*s && !*endptr) ? std::make_optional(value) : std::nullopt;
 }
 
-inline Optional<int32_t> CStringToInt32(const char* s, int base = 10) {
+inline std::optional<int32_t> CStringToInt32(const char* s, int base = 10) {
   char* endptr = nullptr;
   auto value = static_cast<int32_t>(strtol(s, &endptr, base));
-  return (*s && !*endptr) ? base::make_optional(value) : base::nullopt;
+  return (*s && !*endptr) ? std::make_optional(value) : std::nullopt;
 }
 
 // Note: it saturates to 7fffffffffffffff if parsing a hex number >= 0x8000...
-inline Optional<int64_t> CStringToInt64(const char* s, int base = 10) {
+inline std::optional<int64_t> CStringToInt64(const char* s, int base = 10) {
   char* endptr = nullptr;
   auto value = static_cast<int64_t>(strtoll(s, &endptr, base));
-  return (*s && !*endptr) ? base::make_optional(value) : base::nullopt;
+  return (*s && !*endptr) ? std::make_optional(value) : std::nullopt;
 }
 
-inline Optional<uint64_t> CStringToUInt64(const char* s, int base = 10) {
+inline std::optional<uint64_t> CStringToUInt64(const char* s, int base = 10) {
   char* endptr = nullptr;
   auto value = static_cast<uint64_t>(strtoull(s, &endptr, base));
-  return (*s && !*endptr) ? base::make_optional(value) : base::nullopt;
+  return (*s && !*endptr) ? std::make_optional(value) : std::nullopt;
 }
 
 double StrToD(const char* nptr, char** endptr);
 
-inline Optional<double> CStringToDouble(const char* s) {
+inline std::optional<double> CStringToDouble(const char* s) {
   char* endptr = nullptr;
   double value = StrToD(s, &endptr);
-  Optional<double> result(base::nullopt);
+  std::optional<double> result(std::nullopt);
   if (*s != '\0' && *endptr == '\0')
     result = value;
   return result;
 }
 
-inline Optional<uint32_t> StringToUInt32(const std::string& s, int base = 10) {
+inline std::optional<uint32_t> StringToUInt32(const std::string& s,
+                                              int base = 10) {
   return CStringToUInt32(s.c_str(), base);
 }
 
-inline Optional<int32_t> StringToInt32(const std::string& s, int base = 10) {
+inline std::optional<int32_t> StringToInt32(const std::string& s,
+                                            int base = 10) {
   return CStringToInt32(s.c_str(), base);
 }
 
-inline Optional<uint64_t> StringToUInt64(const std::string& s, int base = 10) {
+inline std::optional<uint64_t> StringToUInt64(const std::string& s,
+                                              int base = 10) {
   return CStringToUInt64(s.c_str(), base);
 }
 
-inline Optional<int64_t> StringToInt64(const std::string& s, int base = 10) {
+inline std::optional<int64_t> StringToInt64(const std::string& s,
+                                            int base = 10) {
   return CStringToInt64(s.c_str(), base);
 }
 
-inline Optional<double> StringToDouble(const std::string& s) {
+inline std::optional<double> StringToDouble(const std::string& s) {
   return CStringToDouble(s.c_str());
 }
 
@@ -177,10 +181,10 @@
 
 // For given string and offset Pfinds a line with character for
 // which offset points, what number is this line (starts from 1), and the offset
-// inside this line. returns nullopt if the offset points to
+// inside this line. returns std::nullopt if the offset points to
 // line break character or exceeds string length.
-base::Optional<LineWithOffset> FindLineWithOffset(base::StringView str,
-                                                  uint32_t offset);
+std::optional<LineWithOffset> FindLineWithOffset(base::StringView str,
+                                                 uint32_t offset);
 
 // A helper class to facilitate construction and usage of write-once stack
 // strings.
diff --git a/include/perfetto/ext/base/subprocess.h b/include/perfetto/ext/base/subprocess.h
index 6102832..12121e0 100644
--- a/include/perfetto/ext/base/subprocess.h
+++ b/include/perfetto/ext/base/subprocess.h
@@ -21,6 +21,7 @@
 #include <functional>
 #include <initializer_list>
 #include <mutex>
+#include <optional>
 #include <string>
 #include <thread>
 #include <vector>
@@ -30,7 +31,6 @@
 #include "perfetto/base/platform_handle.h"
 #include "perfetto/base/proc_utils.h"
 #include "perfetto/ext/base/event_fd.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/pipe.h"
 #include "perfetto/ext/base/scoped_file.h"
 
@@ -145,7 +145,7 @@
     // setpgid(0 /*self_pid*/, posix_proc_group_id).
     // This can be used to avoid that subprocesses receive CTRL-C from the
     // terminal, while still living in the same session.
-    base::Optional<pid_t> posix_proc_group_id{};
+    std::optional<pid_t> posix_proc_group_id{};
 #endif
 
     // If non-empty, replaces the environment passed to exec().
diff --git a/include/perfetto/ext/base/threading/channel.h b/include/perfetto/ext/base/threading/channel.h
index d117ff0..9bf42f5 100644
--- a/include/perfetto/ext/base/threading/channel.h
+++ b/include/perfetto/ext/base/threading/channel.h
@@ -18,12 +18,12 @@
 #define INCLUDE_PERFETTO_EXT_BASE_THREADING_CHANNEL_H_
 
 #include <mutex>
+#include <optional>
 
 #include "perfetto/base/compiler.h"
 #include "perfetto/base/platform_handle.h"
 #include "perfetto/ext/base/circular_queue.h"
 #include "perfetto/ext/base/event_fd.h"
-#include "perfetto/ext/base/optional.h"
 
 namespace perfetto {
 namespace base {
@@ -43,24 +43,24 @@
 class Channel {
  public:
   struct ReadResult {
-    ReadResult(base::Optional<T> _item, bool _is_closed)
+    ReadResult(std::optional<T> _item, bool _is_closed)
         : item(std::move(_item)), is_closed(_is_closed) {}
 
     bool operator==(const ReadResult& res) const {
       return item == res.item && is_closed == res.is_closed;
     }
 
-    // The item read from the channel or base::nullopt if the channel is empty.
+    // The item read from the channel or std::nullopt if the channel is empty.
     // If so, callers can use |read_fd| to be notified when a read operation
     // would succeed.
-    base::Optional<T> item;
+    std::optional<T> item;
 
     // Indicates the channel is closed. Readers can continue to read from the
     // channel and any buffered elements will be correctly returned. Moreover,
     // any future reads will also have |is_closed| == true and |read_fd| will be
     // ready forever.
     //
-    // Once a ReadResult is returned with |item| == base::nullopt and
+    // Once a ReadResult is returned with |item| == std::nullopt and
     // |is_closed| == true, no further values will ever be returned.
     bool is_closed;
   };
@@ -103,7 +103,7 @@
   PERFETTO_WARN_UNUSED_RESULT ReadResult ReadNonBlocking() {
     std::lock_guard<std::mutex> lock(mutex_);
     if (elements_.empty()) {
-      return ReadResult(base::nullopt, is_closed_);
+      return ReadResult(std::nullopt, is_closed_);
     }
     if (elements_.capacity() == elements_.size()) {
       write_fd_.Notify();
diff --git a/include/perfetto/ext/base/threading/future_combinators.h b/include/perfetto/ext/base/threading/future_combinators.h
index 40f7722..d726c66 100644
--- a/include/perfetto/ext/base/threading/future_combinators.h
+++ b/include/perfetto/ext/base/threading/future_combinators.h
@@ -17,11 +17,11 @@
 #ifndef INCLUDE_PERFETTO_EXT_BASE_THREADING_FUTURE_COMBINATORS_H_
 #define INCLUDE_PERFETTO_EXT_BASE_THREADING_FUTURE_COMBINATORS_H_
 
+#include <optional>
 #include <utility>
 #include <vector>
 
 #include "perfetto/base/status.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/threading/poll.h"
 
 namespace perfetto {
@@ -57,17 +57,17 @@
     PERFETTO_CHECK((first_ && second_fn_) || second_);
     if (first_) {
       ASSIGN_OR_RETURN_IF_PENDING_FUTURE(res, first_->Poll(context));
-      first_ = nullopt;
+      first_ = std::nullopt;
       second_ = (*second_fn_)(std::move(res));
-      second_fn_ = base::nullopt;
+      second_fn_ = std::nullopt;
     }
     return second_->Poll(context);
   }
 
  private:
-  Optional<Future<A>> first_;
-  Optional<Function> second_fn_;
-  Optional<Future<B>> second_;
+  std::optional<Future<A>> first_;
+  std::optional<Function> second_fn_;
+  std::optional<Future<B>> second_;
 };
 
 }  // namespace base
diff --git a/include/perfetto/ext/base/threading/poll.h b/include/perfetto/ext/base/threading/poll.h
index 5989361..5a4b7e3 100644
--- a/include/perfetto/ext/base/threading/poll.h
+++ b/include/perfetto/ext/base/threading/poll.h
@@ -17,10 +17,11 @@
 #ifndef INCLUDE_PERFETTO_EXT_BASE_THREADING_POLL_H_
 #define INCLUDE_PERFETTO_EXT_BASE_THREADING_POLL_H_
 
+#include <optional>
+
 #include <variant>
 #include "perfetto/base/flat_set.h"
 #include "perfetto/base/platform_handle.h"
-#include "perfetto/ext/base/optional.h"
 
 namespace perfetto {
 namespace base {
diff --git a/include/perfetto/ext/base/threading/spawn.h b/include/perfetto/ext/base/threading/spawn.h
index bf73fb9..df2f8ec 100644
--- a/include/perfetto/ext/base/threading/spawn.h
+++ b/include/perfetto/ext/base/threading/spawn.h
@@ -22,6 +22,7 @@
 #include <functional>
 #include <memory>
 #include <mutex>
+#include <optional>
 #include <utility>
 #include <vector>
 
@@ -31,7 +32,6 @@
 #include "perfetto/base/task_runner.h"
 #include "perfetto/ext/base/event_fd.h"
 #include "perfetto/ext/base/flat_hash_map.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/thread_checker.h"
 #include "perfetto/ext/base/threading/channel.h"
 #include "perfetto/ext/base/threading/future.h"
@@ -110,7 +110,7 @@
     std::function<Stream<T>()> fn) {
   class AllVoidCollector : public Collector<FVoid, FVoid> {
    public:
-    Optional<FVoid> OnNext(FVoid) override { return nullopt; }
+    std::optional<FVoid> OnNext(FVoid) override { return std::nullopt; }
     FVoid OnDone() override { return FVoid(); }
   };
   auto channel = std::make_shared<Channel<T>>(4);
diff --git a/include/perfetto/ext/base/threading/stream_combinators.h b/include/perfetto/ext/base/threading/stream_combinators.h
index 5485139..2b7b69e 100644
--- a/include/perfetto/ext/base/threading/stream_combinators.h
+++ b/include/perfetto/ext/base/threading/stream_combinators.h
@@ -18,11 +18,11 @@
 #define INCLUDE_PERFETTO_EXT_BASE_THREADING_STREAM_COMBINATORS_H_
 
 #include <memory>
+#include <optional>
 #include <utility>
 #include <vector>
 
 #include "perfetto/base/status.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/status_or.h"
 #include "perfetto/ext/base/threading/future_combinators.h"
 #include "perfetto/ext/base/threading/poll.h"
@@ -87,14 +87,14 @@
       future_ = map_fn_(std::move(res.item()));
     }
     ASSIGN_OR_RETURN_IF_PENDING_FUTURE(res, future_->Poll(context));
-    future_ = nullopt;
+    future_ = std::nullopt;
     return res;
   }
 
  private:
   Stream<T> stream_;
   Function map_fn_;
-  Optional<Future<U>> future_;
+  std::optional<Future<U>> future_;
 };
 
 // Implementation of a StreamPollable for creating a concatenating two streams
@@ -111,13 +111,13 @@
       if (!res.IsDone()) {
         return res.item();
       }
-      first_ = base::nullopt;
+      first_ = std::nullopt;
     }
     return second_.PollNext(context);
   }
 
  private:
-  base::Optional<Stream<T>> first_;
+  std::optional<Stream<T>> first_;
   Stream<T> second_;
 };
 
@@ -140,7 +140,7 @@
       if (!stream) {
         continue;
       }
-      Optional<PollContext> ctx = PollContextForStream(upstream, i);
+      std::optional<PollContext> ctx = PollContextForStream(upstream, i);
       if (!ctx) {
         continue;
       }
@@ -154,7 +154,7 @@
       }
       // StreamPollable has returned EOF. Clear it and the registered handles
       // out.
-      stream = nullopt;
+      stream = std::nullopt;
       ++eof_streams_;
       registered_handles_[i].clear();
     }
@@ -172,8 +172,8 @@
   }
 
  private:
-  Optional<PollContext> PollContextForStream(PollContext* upstream,
-                                             uint32_t stream_idx) {
+  std::optional<PollContext> PollContextForStream(PollContext* upstream,
+                                                  uint32_t stream_idx) {
     FlatSet<PlatformHandle>& state = registered_handles_[stream_idx];
     if (state.empty()) {
       return PollContext(&state, &upstream->ready_handles());
@@ -184,10 +184,10 @@
         return PollContext(&state, &upstream->ready_handles());
       }
     }
-    return base::nullopt;
+    return std::nullopt;
   }
 
-  std::vector<Optional<Stream<T>>> streams_;
+  std::vector<std::optional<Stream<T>>> streams_;
   std::vector<FlatSet<PlatformHandle>> registered_handles_;
   uint32_t eof_streams_ = 0;
 };
@@ -226,12 +226,12 @@
 
   // Receives the next item from a Stream<T>. If the wrapping Future<U> can be
   // completed, returns the a value U which completes that future. Otherwise,
-  // returns base::nullopt.
-  virtual Optional<U> OnNext(T value) = 0;
+  // returns std::nullopt.
+  virtual std::optional<U> OnNext(T value) = 0;
 
   // Called when the stream has completed and returns the |U| which will be
   // used to complete the future. This method will only be called if OnNext
-  // returned nullopt for every element in the stream.
+  // returned std::nullopt for every element in the stream.
   virtual U OnDone() = 0;
 };
 
@@ -250,7 +250,7 @@
       if (res.IsDone()) {
         return collector_->OnDone();
       }
-      Optional<U> collected = collector_->OnNext(std::move(res.item()));
+      std::optional<U> collected = collector_->OnNext(std::move(res.item()));
       if (collected.has_value()) {
         return std::move(collected.value());
       }
@@ -267,8 +267,8 @@
  public:
   ~AllOkCollectorImpl() override;
 
-  Optional<Status> OnNext(Status status) override {
-    return status.ok() ? nullopt : make_optional(std::move(status));
+  std::optional<Status> OnNext(Status status) override {
+    return status.ok() ? std::nullopt : std::make_optional(std::move(status));
   }
   Status OnDone() override { return OkStatus(); }
 };
@@ -277,15 +277,15 @@
 template <typename T>
 class FutureCheckedCollectorImpl : public Collector<T, T> {
  public:
-  Optional<T> OnNext(T value) override {
+  std::optional<T> OnNext(T value) override {
     PERFETTO_CHECK(!prev_value_);
     prev_value_ = value;
-    return nullopt;
+    return std::nullopt;
   }
   T OnDone() override { return *prev_value_; }
 
  private:
-  Optional<T> prev_value_;
+  std::optional<T> prev_value_;
 };
 
 // Implementation for |StatusOrVectorCollector|.
@@ -293,13 +293,13 @@
 class StatusOrVectorCollectorImpl
     : public Collector<base::StatusOr<T>, base::StatusOr<std::vector<T>>> {
  public:
-  Optional<base::StatusOr<std::vector<T>>> OnNext(
+  std::optional<base::StatusOr<std::vector<T>>> OnNext(
       base::StatusOr<T> val_or) override {
     if (!val_or.ok()) {
-      return make_optional(val_or.status());
+      return std::make_optional(val_or.status());
     }
     values_.emplace_back(std::move(val_or.value()));
-    return nullopt;
+    return std::nullopt;
   }
   base::StatusOr<std::vector<T>> OnDone() override {
     return std::move(values_);
diff --git a/include/perfetto/ext/base/threading/thread_pool.h b/include/perfetto/ext/base/threading/thread_pool.h
index 1a4b153..e08baaa 100644
--- a/include/perfetto/ext/base/threading/thread_pool.h
+++ b/include/perfetto/ext/base/threading/thread_pool.h
@@ -21,11 +21,11 @@
 #include <functional>
 #include <list>
 #include <mutex>
+#include <optional>
 #include <thread>
 #include <vector>
 
 #include "perfetto/base/task_runner.h"
-#include "perfetto/ext/base/optional.h"
 
 namespace perfetto {
 namespace base {
diff --git a/include/perfetto/ext/base/threading/util.h b/include/perfetto/ext/base/threading/util.h
index 34dccdb..7b2465e 100644
--- a/include/perfetto/ext/base/threading/util.h
+++ b/include/perfetto/ext/base/threading/util.h
@@ -19,13 +19,13 @@
 
 #include <functional>
 #include <memory>
+#include <optional>
 #include <type_traits>
 #include <utility>
 #include <vector>
 
 #include "perfetto/base/status.h"
 #include "perfetto/base/task_runner.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/threading/channel.h"
 #include "perfetto/ext/base/threading/future.h"
 #include "perfetto/ext/base/threading/poll.h"
@@ -104,7 +104,7 @@
 
 // Creates a Stream<T> which yields the result of executing |fn| on |pool|
 // repeatedly. The returned stream only completes when |fn| returns
-// base::nullopt.
+// std::nullopt.
 //
 // The intended usage of this function is to schedule CPU intensive work on a
 // background thread pool and receive regular "updates" on the progress by:
@@ -112,13 +112,13 @@
 // b) returning some indication of progress/partial results through |T|.
 template <typename T>
 Stream<T> RunOnThreadPool(ThreadPool* pool,
-                          std::function<base::Optional<T>()> fn) {
+                          std::function<std::optional<T>()> fn) {
   class RunOnPoolImpl : public StreamPollable<T> {
    public:
     explicit RunOnPoolImpl(ThreadPool* pool,
-                           std::function<base::Optional<T>()> fn)
+                           std::function<std::optional<T>()> fn)
         : pool_(pool),
-          fn_(std::make_shared<std::function<base::Optional<T>()>>(
+          fn_(std::make_shared<std::function<std::optional<T>()>>(
               std::move(fn))),
           channel_(new Channel<T>(1)),
           channel_stream_(ReadChannelStream(channel_.get())) {
@@ -150,7 +150,7 @@
     }
 
     ThreadPool* pool_ = nullptr;
-    std::shared_ptr<std::function<base::Optional<T>()>> fn_;
+    std::shared_ptr<std::function<std::optional<T>()>> fn_;
     std::shared_ptr<Channel<T>> channel_;
     base::Stream<T> channel_stream_;
   };
@@ -166,9 +166,9 @@
 Future<T> RunOnceOnThreadPool(ThreadPool* pool, std::function<T()> fn) {
   return RunOnThreadPool<T>(
              pool,
-             [done = false, fn = std::move(fn)]() mutable -> base::Optional<T> {
+             [done = false, fn = std::move(fn)]() mutable -> std::optional<T> {
                if (done) {
-                 return base::nullopt;
+                 return std::nullopt;
                }
                done = true;
                return fn();
diff --git a/include/perfetto/ext/base/uuid.h b/include/perfetto/ext/base/uuid.h
index 5e2e258..2bf5f5b 100644
--- a/include/perfetto/ext/base/uuid.h
+++ b/include/perfetto/ext/base/uuid.h
@@ -20,10 +20,9 @@
 #include <string.h>
 #include <array>
 #include <cstdint>
+#include <optional>
 #include <string>
 
-#include "perfetto/ext/base/optional.h"
-
 namespace perfetto {
 namespace base {
 
diff --git a/include/perfetto/ext/trace_processor/importers/memory_tracker/graph_processor.h b/include/perfetto/ext/trace_processor/importers/memory_tracker/graph_processor.h
index f7b02e7..2d702f7 100644
--- a/include/perfetto/ext/trace_processor/importers/memory_tracker/graph_processor.h
+++ b/include/perfetto/ext/trace_processor/importers/memory_tracker/graph_processor.h
@@ -80,7 +80,7 @@
   static void PropagateNumericsAndDiagnosticsRecursively(
       GlobalNodeGraph::Node* node);
 
-  static base::Optional<uint64_t> AggregateSizeForDescendantNode(
+  static std::optional<uint64_t> AggregateSizeForDescendantNode(
       GlobalNodeGraph::Node* root,
       GlobalNodeGraph::Node* descendant);
 
diff --git a/infra/perfetto.dev/src/gen_sql_tables_reference.js b/infra/perfetto.dev/src/gen_sql_tables_reference.js
index 9af1d32..2f77cea 100644
--- a/infra/perfetto.dev/src/gen_sql_tables_reference.js
+++ b/infra/perfetto.dev/src/gen_sql_tables_reference.js
@@ -111,7 +111,7 @@
     if (m = line.match(/^\s*C\(([^,]+)\s*,\s*(\w+)/)) {
       const col = getOrCreateColumn(/*name=*/ m[2]);
       col.type = m[1];
-      if (m = col.type.match(/Optional<(.*)>/)) {
+      if (m = col.type.match(/std::optional<(.*)>/)) {
         col.type = m[1];
         col.optional = true;
       }
@@ -168,7 +168,7 @@
     // C(StringPool::Id, name)                                    \
     // C(StackProfileMappingTable::Id, mapping)                   \
     // C(int64_t, rel_pc)                                         \
-    // C(base::Optional<uint32_t>, symbol_set_id)
+    // C(std::optional<uint32_t>, symbol_set_id)
     //
     // Where PERFETTO_TP_STACK_PROFILE_FRAME_DEF is |tableDefName|.
     let pattern = `(^[ ]*//.*\n)*`;
diff --git a/python/generators/trace_processor_table/serialize.py b/python/generators/trace_processor_table/serialize.py
index 4aee5b5..0a8715c 100644
--- a/python/generators/trace_processor_table/serialize.py
+++ b/python/generators/trace_processor_table/serialize.py
@@ -462,15 +462,15 @@
     {self.foreach_col(ColumnSerializer.shrink_to_fit)}
   }}
 
-  base::Optional<ConstRowReference> FindById(Id find_id) const {{
-    base::Optional<uint32_t> row = id().IndexOf(find_id);
-    return row ? base::make_optional(ConstRowReference(this, *row))
-               : base::nullopt;
+  std::optional<ConstRowReference> FindById(Id find_id) const {{
+    std::optional<uint32_t> row = id().IndexOf(find_id);
+    return row ? std::make_optional(ConstRowReference(this, *row))
+               : std::nullopt;
   }}
 
-  base::Optional<RowReference> FindById(Id find_id) {{
-    base::Optional<uint32_t> row = id().IndexOf(find_id);
-    return row ? base::make_optional(RowReference(this, *row)) : base::nullopt;
+  std::optional<RowReference> FindById(Id find_id) {{
+    std::optional<uint32_t> row = id().IndexOf(find_id);
+    return row ? std::make_optional(RowReference(this, *row)) : std::nullopt;
   }}
 
   IdAndRow Insert(const Row& row) {{
diff --git a/python/generators/trace_processor_table/util.py b/python/generators/trace_processor_table/util.py
index b6c38d8..015f02c 100644
--- a/python/generators/trace_processor_table/util.py
+++ b/python/generators/trace_processor_table/util.py
@@ -62,7 +62,7 @@
     else:
       cpp_type = self.cpp_type
     if self.is_optional:
-      return f'base::Optional<{cpp_type}>'
+      return f'std::optional<{cpp_type}>'
     return cpp_type
 
 
diff --git a/src/base/base64.cc b/src/base/base64.cc
index bb2e3c4..6b2236b 100644
--- a/src/base/base64.cc
+++ b/src/base/base64.cc
@@ -136,17 +136,17 @@
   return static_cast<ssize_t>(wr_size);
 }
 
-Optional<std::string> Base64Decode(const char* src, size_t src_size) {
+std::optional<std::string> Base64Decode(const char* src, size_t src_size) {
   std::string dst;
   dst.resize(Base64DecSize(src_size));
   auto res = Base64Decode(src, src_size, reinterpret_cast<uint8_t*>(&dst[0]),
                           dst.size());
   if (res < 0)
-    return nullopt;  // Decoding error.
+    return std::nullopt;  // Decoding error.
 
   PERFETTO_CHECK(res <= static_cast<ssize_t>(dst.size()));
   dst.resize(static_cast<size_t>(res));
-  return base::make_optional(dst);
+  return std::make_optional(dst);
 }
 
 }  // namespace base
diff --git a/src/base/base64_unittest.cc b/src/base/base64_unittest.cc
index fb131d9..f01b13c 100644
--- a/src/base/base64_unittest.cc
+++ b/src/base/base64_unittest.cc
@@ -341,14 +341,14 @@
 
   for (size_t i = 0; i < ArraySize(kPatterns); ++i) {
     const auto& p = kPatterns[i];
-    Optional<std::string> dec = Base64Decode(StringView(p.encoded));
+    std::optional<std::string> dec = Base64Decode(StringView(p.encoded));
     EXPECT_TRUE(dec.has_value());
     EXPECT_EQ(dec.value(), StringView(p.decoded, p.decoded_len).ToStdString());
   }
 
   // Error cases:
-  EXPECT_EQ(Base64Decode("Z"), nullopt);
-  EXPECT_EQ(Base64Decode("Zm9vY"), nullopt);
+  EXPECT_EQ(Base64Decode("Z"), std::nullopt);
+  EXPECT_EQ(Base64Decode("Zm9vY"), std::nullopt);
 
   uint8_t buf[4];
   EXPECT_EQ(Base64Decode("", 0, buf, 2), 0);       // Valid, 0 len.
diff --git a/src/base/file_utils.cc b/src/base/file_utils.cc
index 9f70ad7..e7d4c94 100644
--- a/src/base/file_utils.cc
+++ b/src/base/file_utils.cc
@@ -21,6 +21,7 @@
 
 #include <algorithm>
 #include <deque>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -28,7 +29,6 @@
 #include "perfetto/base/logging.h"
 #include "perfetto/base/platform_handle.h"
 #include "perfetto/base/status.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/platform.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/utils.h"
@@ -54,11 +54,11 @@
   return FindClose(h) ? 0 : -1;
 }
 
-Optional<std::wstring> ToUtf16(const std::string str) {
+std::optional<std::wstring> ToUtf16(const std::string str) {
   int len = MultiByteToWideChar(CP_UTF8, 0, str.data(),
                                 static_cast<int>(str.size()), nullptr, 0);
   if (len < 0) {
-    return base::nullopt;
+    return std::nullopt;
   }
   std::vector<wchar_t> tmp;
   tmp.resize(static_cast<std::vector<wchar_t>::size_type>(len));
@@ -66,7 +66,7 @@
       MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast<int>(str.size()),
                           tmp.data(), static_cast<int>(tmp.size()));
   if (len < 0) {
-    return base::nullopt;
+    return std::nullopt;
   }
   PERFETTO_CHECK(static_cast<std::vector<wchar_t>::size_type>(len) ==
                  tmp.size());
diff --git a/src/base/http/http_server.cc b/src/base/http/http_server.cc
index 3db6ae2..2efe9f7 100644
--- a/src/base/http/http_server.cc
+++ b/src/base/http/http_server.cc
@@ -565,12 +565,12 @@
 
 HttpServerConnection::~HttpServerConnection() = default;
 
-Optional<StringView> HttpRequest::GetHeader(StringView name) const {
+std::optional<StringView> HttpRequest::GetHeader(StringView name) const {
   for (size_t i = 0; i < num_headers; i++) {
     if (headers[i].name.CaseInsensitiveEq(name))
       return headers[i].value;
   }
-  return nullopt;
+  return std::nullopt;
 }
 
 HttpRequestHandler::~HttpRequestHandler() = default;
diff --git a/src/base/string_utils.cc b/src/base/string_utils.cc
index 851b60c..ba6e52b 100644
--- a/src/base/string_utils.cc
+++ b/src/base/string_utils.cc
@@ -245,8 +245,8 @@
   return res;
 }
 
-base::Optional<LineWithOffset> FindLineWithOffset(base::StringView str,
-                                                  uint32_t offset) {
+std::optional<LineWithOffset> FindLineWithOffset(base::StringView str,
+                                                 uint32_t offset) {
   static constexpr char kNewLine = '\n';
   uint32_t line_offset = 0;
   uint32_t line_count = 1;
@@ -265,7 +265,7 @@
       return LineWithOffset{line, offset - line_offset, line_count};
     }
   }
-  return base::nullopt;
+  return std::nullopt;
 }
 
 }  // namespace base
diff --git a/src/base/string_utils_unittest.cc b/src/base/string_utils_unittest.cc
index 2cc4b06..f65da9f 100644
--- a/src/base/string_utils_unittest.cc
+++ b/src/base/string_utils_unittest.cc
@@ -16,10 +16,9 @@
 
 #include "perfetto/ext/base/string_utils.h"
 
+#include <optional>
 #include "test/gtest_and_gmock.h"
 
-#include "perfetto/ext/base/optional.h"
-
 namespace perfetto {
 namespace base {
 namespace {
@@ -50,23 +49,23 @@
 }
 
 TEST(StringUtilsTest, CStringToUInt32) {
-  EXPECT_EQ(CStringToUInt32("0"), make_optional<uint32_t>(0U));
-  EXPECT_EQ(CStringToUInt32("1"), make_optional<uint32_t>(1U));
-  EXPECT_EQ(CStringToUInt32("42"), make_optional<uint32_t>(42U));
-  EXPECT_EQ(CStringToUInt32(""), nullopt);
-  EXPECT_EQ(CStringToUInt32("!?"), nullopt);
-  EXPECT_EQ(CStringToUInt32("abc"), nullopt);
-  EXPECT_EQ(CStringToUInt32("123 abc"), nullopt);
+  EXPECT_EQ(CStringToUInt32("0"), std::make_optional<uint32_t>(0U));
+  EXPECT_EQ(CStringToUInt32("1"), std::make_optional<uint32_t>(1U));
+  EXPECT_EQ(CStringToUInt32("42"), std::make_optional<uint32_t>(42U));
+  EXPECT_EQ(CStringToUInt32(""), std::nullopt);
+  EXPECT_EQ(CStringToUInt32("!?"), std::nullopt);
+  EXPECT_EQ(CStringToUInt32("abc"), std::nullopt);
+  EXPECT_EQ(CStringToUInt32("123 abc"), std::nullopt);
 }
 
 TEST(StringUtilsTest, CStringToInt32) {
-  EXPECT_EQ(CStringToInt32("0"), make_optional<int32_t>(0));
-  EXPECT_EQ(CStringToInt32("1"), make_optional<int32_t>(1));
-  EXPECT_EQ(CStringToInt32("-42"), make_optional<int32_t>(-42));
-  EXPECT_EQ(CStringToInt32(""), nullopt);
-  EXPECT_EQ(CStringToInt32("!?"), nullopt);
-  EXPECT_EQ(CStringToInt32("abc"), nullopt);
-  EXPECT_EQ(CStringToInt32("123 abc"), nullopt);
+  EXPECT_EQ(CStringToInt32("0"), std::make_optional<int32_t>(0));
+  EXPECT_EQ(CStringToInt32("1"), std::make_optional<int32_t>(1));
+  EXPECT_EQ(CStringToInt32("-42"), std::make_optional<int32_t>(-42));
+  EXPECT_EQ(CStringToInt32(""), std::nullopt);
+  EXPECT_EQ(CStringToInt32("!?"), std::nullopt);
+  EXPECT_EQ(CStringToInt32("abc"), std::nullopt);
+  EXPECT_EQ(CStringToInt32("123 abc"), std::nullopt);
 }
 
 TEST(StringUtilsTest, CStringToDouble) {
@@ -74,66 +73,68 @@
   EXPECT_DOUBLE_EQ(CStringToDouble("1").value(), 1l);
   EXPECT_DOUBLE_EQ(CStringToDouble("-42").value(), -42l);
   EXPECT_DOUBLE_EQ(CStringToDouble("-42.5").value(), -42.5l);
-  EXPECT_EQ(CStringToDouble(""), nullopt);
-  EXPECT_EQ(CStringToDouble("!?"), nullopt);
-  EXPECT_EQ(CStringToDouble("abc"), nullopt);
-  EXPECT_EQ(CStringToDouble("123 abc"), nullopt);
+  EXPECT_EQ(CStringToDouble(""), std::nullopt);
+  EXPECT_EQ(CStringToDouble("!?"), std::nullopt);
+  EXPECT_EQ(CStringToDouble("abc"), std::nullopt);
+  EXPECT_EQ(CStringToDouble("123 abc"), std::nullopt);
 }
 
 TEST(StringUtilsTest, StringToUInt32) {
-  EXPECT_EQ(StringToUInt32("0"), make_optional<uint32_t>(0U));
-  EXPECT_EQ(StringToUInt32("1"), make_optional<uint32_t>(1U));
-  EXPECT_EQ(StringToUInt32("42"), make_optional<uint32_t>(42U));
-  EXPECT_EQ(StringToUInt32("a", 16), make_optional<uint32_t>(10U));
+  EXPECT_EQ(StringToUInt32("0"), std::make_optional<uint32_t>(0U));
+  EXPECT_EQ(StringToUInt32("1"), std::make_optional<uint32_t>(1U));
+  EXPECT_EQ(StringToUInt32("42"), std::make_optional<uint32_t>(42U));
+  EXPECT_EQ(StringToUInt32("a", 16), std::make_optional<uint32_t>(10U));
   EXPECT_EQ(StringToUInt32("fffffff0", 16),
-            make_optional<uint32_t>(0xfffffff0));
-  EXPECT_EQ(StringToUInt32(""), nullopt);
-  EXPECT_EQ(StringToUInt32("!?"), nullopt);
-  EXPECT_EQ(StringToUInt32("abc"), nullopt);
-  EXPECT_EQ(StringToUInt32("123 abc"), nullopt);
-  EXPECT_EQ(StringToUInt32("beefz", 16), nullopt);
+            std::make_optional<uint32_t>(0xfffffff0));
+  EXPECT_EQ(StringToUInt32(""), std::nullopt);
+  EXPECT_EQ(StringToUInt32("!?"), std::nullopt);
+  EXPECT_EQ(StringToUInt32("abc"), std::nullopt);
+  EXPECT_EQ(StringToUInt32("123 abc"), std::nullopt);
+  EXPECT_EQ(StringToUInt32("beefz", 16), std::nullopt);
 }
 
 TEST(StringUtilsTest, StringToInt32) {
-  EXPECT_EQ(StringToInt32("0"), make_optional<int32_t>(0));
-  EXPECT_EQ(StringToInt32("1"), make_optional<int32_t>(1));
-  EXPECT_EQ(StringToInt32("-42"), make_optional<int32_t>(-42));
-  EXPECT_EQ(StringToInt32("42", 16), make_optional<int32_t>(0x42));
-  EXPECT_EQ(StringToInt32("7ffffffe", 16), make_optional<int32_t>(0x7ffffffe));
-  EXPECT_EQ(StringToInt32(""), nullopt);
-  EXPECT_EQ(StringToInt32("!?"), nullopt);
-  EXPECT_EQ(StringToInt32("abc"), nullopt);
-  EXPECT_EQ(StringToInt32("123 abc"), nullopt);
-  EXPECT_EQ(StringToInt32("beefz", 16), nullopt);
+  EXPECT_EQ(StringToInt32("0"), std::make_optional<int32_t>(0));
+  EXPECT_EQ(StringToInt32("1"), std::make_optional<int32_t>(1));
+  EXPECT_EQ(StringToInt32("-42"), std::make_optional<int32_t>(-42));
+  EXPECT_EQ(StringToInt32("42", 16), std::make_optional<int32_t>(0x42));
+  EXPECT_EQ(StringToInt32("7ffffffe", 16),
+            std::make_optional<int32_t>(0x7ffffffe));
+  EXPECT_EQ(StringToInt32(""), std::nullopt);
+  EXPECT_EQ(StringToInt32("!?"), std::nullopt);
+  EXPECT_EQ(StringToInt32("abc"), std::nullopt);
+  EXPECT_EQ(StringToInt32("123 abc"), std::nullopt);
+  EXPECT_EQ(StringToInt32("beefz", 16), std::nullopt);
 }
 
 TEST(StringUtilsTest, StringToUInt64) {
-  EXPECT_EQ(StringToUInt64("0"), make_optional<uint64_t>(0u));
-  EXPECT_EQ(StringToUInt64("1"), make_optional<uint64_t>(1u));
+  EXPECT_EQ(StringToUInt64("0"), std::make_optional<uint64_t>(0u));
+  EXPECT_EQ(StringToUInt64("1"), std::make_optional<uint64_t>(1u));
   EXPECT_EQ(StringToUInt64("5000000000"),
-            make_optional<uint64_t>(5000000000ULL));
+            std::make_optional<uint64_t>(5000000000ULL));
   EXPECT_EQ(StringToUInt64("7ffffffffffffffe", 16),
-            make_optional<uint64_t>(0x7ffffffffffffffeULL));
+            std::make_optional<uint64_t>(0x7ffffffffffffffeULL));
   EXPECT_EQ(StringToUInt64("9ffffffffffffffe", 16),
-            make_optional<uint64_t>(0x9ffffffffffffffeULL));
-  EXPECT_EQ(StringToUInt64(""), nullopt);
-  EXPECT_EQ(StringToUInt64("abc"), nullopt);
-  EXPECT_EQ(StringToUInt64("beefz", 16), nullopt);
+            std::make_optional<uint64_t>(0x9ffffffffffffffeULL));
+  EXPECT_EQ(StringToUInt64(""), std::nullopt);
+  EXPECT_EQ(StringToUInt64("abc"), std::nullopt);
+  EXPECT_EQ(StringToUInt64("beefz", 16), std::nullopt);
 }
 
 TEST(StringUtilsTest, StringToInt64) {
-  EXPECT_EQ(StringToInt64("0"), make_optional<int64_t>(0));
-  EXPECT_EQ(StringToInt64("1"), make_optional<int64_t>(1));
+  EXPECT_EQ(StringToInt64("0"), std::make_optional<int64_t>(0));
+  EXPECT_EQ(StringToInt64("1"), std::make_optional<int64_t>(1));
   EXPECT_EQ(StringToInt64("-5000000000"),
-            make_optional<int64_t>(-5000000000LL));
-  EXPECT_EQ(StringToInt64("5000000000"), make_optional<int64_t>(5000000000LL));
+            std::make_optional<int64_t>(-5000000000LL));
+  EXPECT_EQ(StringToInt64("5000000000"),
+            std::make_optional<int64_t>(5000000000LL));
   EXPECT_EQ(StringToInt64("7ffffffffffffffe", 16),
-            make_optional<int64_t>(0x7ffffffffffffffeLL));
+            std::make_optional<int64_t>(0x7ffffffffffffffeLL));
   EXPECT_EQ(StringToInt64("9ffffffe", 16),
-            make_optional<int64_t>(0x9ffffffeLL));
-  EXPECT_EQ(StringToInt64(""), nullopt);
-  EXPECT_EQ(StringToInt64("abc"), nullopt);
-  EXPECT_EQ(StringToInt64("beefz", 16), nullopt);
+            std::make_optional<int64_t>(0x9ffffffeLL));
+  EXPECT_EQ(StringToInt64(""), std::nullopt);
+  EXPECT_EQ(StringToInt64("abc"), std::nullopt);
+  EXPECT_EQ(StringToInt64("beefz", 16), std::nullopt);
 }
 
 TEST(StringUtilsTest, StringToDouble) {
@@ -143,13 +144,13 @@
   EXPECT_DOUBLE_EQ(StringToDouble("-42.5").value(), -42.5l);
   EXPECT_DOUBLE_EQ(StringToDouble("0.5").value(), .5l);
   EXPECT_DOUBLE_EQ(StringToDouble(".5").value(), .5l);
-  EXPECT_EQ(StringToDouble(""), nullopt);
-  EXPECT_EQ(StringToDouble("!?"), nullopt);
-  EXPECT_EQ(StringToDouble("abc"), nullopt);
-  EXPECT_EQ(StringToDouble("123 abc"), nullopt);
-  EXPECT_EQ(StringToDouble("124,456"), nullopt);
-  EXPECT_EQ(StringToDouble("4 2"), nullopt);
-  EXPECT_EQ(StringToDouble(" - 42"), nullopt);
+  EXPECT_EQ(StringToDouble(""), std::nullopt);
+  EXPECT_EQ(StringToDouble("!?"), std::nullopt);
+  EXPECT_EQ(StringToDouble("abc"), std::nullopt);
+  EXPECT_EQ(StringToDouble("123 abc"), std::nullopt);
+  EXPECT_EQ(StringToDouble("124,456"), std::nullopt);
+  EXPECT_EQ(StringToDouble("4 2"), std::nullopt);
+  EXPECT_EQ(StringToDouble(" - 42"), std::nullopt);
 }
 
 TEST(StringUtilsTest, StartsWith) {
diff --git a/src/base/threading/channel_unittest.cc b/src/base/threading/channel_unittest.cc
index 7acb355..2c5e45e 100644
--- a/src/base/threading/channel_unittest.cc
+++ b/src/base/threading/channel_unittest.cc
@@ -15,12 +15,13 @@
  */
 
 #include "perfetto/ext/base/threading/channel.h"
+
 #include <array>
 #include <memory>
+#include <optional>
 
 #include "perfetto/base/platform_handle.h"
 #include "perfetto/ext/base/file_utils.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/utils.h"
 #include "test/gtest_and_gmock.h"
 
@@ -71,7 +72,7 @@
   ASSERT_TRUE(IsReady(channel.read_fd()));
 
   ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(100, false));
-  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(base::nullopt, false));
+  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(std::nullopt, false));
 
   ASSERT_TRUE(IsReady(channel.write_fd()));
   ASSERT_FALSE(IsReady(channel.read_fd()));
@@ -98,7 +99,7 @@
   ASSERT_TRUE(IsReady(channel.write_fd()));
   ASSERT_FALSE(IsReady(channel.read_fd()));
 
-  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(base::nullopt, false));
+  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(std::nullopt, false));
   ASSERT_TRUE(IsReady(channel.write_fd()));
   ASSERT_FALSE(IsReady(channel.read_fd()));
 }
@@ -106,13 +107,13 @@
 TEST(ChannelUnittest, CloseEmptyChannel) {
   Channel<int> channel(1);
 
-  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(base::nullopt, false));
+  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(std::nullopt, false));
   ASSERT_FALSE(IsReady(channel.read_fd()));
 
   channel.Close();
 
-  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(base::nullopt, true));
-  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(base::nullopt, true));
+  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(std::nullopt, true));
+  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(std::nullopt, true));
 
   ASSERT_TRUE(IsReady(channel.read_fd()));
   ASSERT_TRUE(IsReady(channel.read_fd()));
@@ -139,12 +140,12 @@
 
 TEST(ChannelUnittest, ReadAfterClose) {
   Channel<int> channel(1);
-  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(base::nullopt, false));
+  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(std::nullopt, false));
   ASSERT_EQ(channel.WriteNonBlocking(100), WriteResult(true, false));
   channel.Close();
 
   ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(100, true));
-  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(base::nullopt, true));
+  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(std::nullopt, true));
 }
 
 TEST(ChannelUnittest, WriteAfterClose) {
@@ -164,7 +165,7 @@
   channel.Close();
   ASSERT_TRUE(IsReady(channel.write_fd()));
   ASSERT_TRUE(IsReady(channel.write_fd()));
-  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(base::nullopt, true));
+  ASSERT_EQ(channel.ReadNonBlocking(), ReadResult(std::nullopt, true));
   ASSERT_TRUE(IsReady(channel.write_fd()));
   ASSERT_TRUE(IsReady(channel.read_fd()));
 }
diff --git a/src/base/threading/spawn.cc b/src/base/threading/spawn.cc
index 4fd246a..e727f84 100644
--- a/src/base/threading/spawn.cc
+++ b/src/base/threading/spawn.cc
@@ -16,8 +16,9 @@
 
 #include "perfetto/ext/base/threading/spawn.h"
 
+#include <optional>
+
 #include "perfetto/base/task_runner.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/thread_checker.h"
 #include "perfetto/ext/base/threading/future.h"
 #include "perfetto/ext/base/threading/poll.h"
@@ -74,7 +75,7 @@
   }
 
   void ClearFutureAndWatches(FlatSet<PlatformHandle> interested) {
-    future_ = nullopt;
+    future_ = std::nullopt;
     for (PlatformHandle fd : interested) {
       task_runner_->RemoveFileDescriptorWatch(fd);
     }
@@ -94,7 +95,7 @@
 
   TaskRunner* const task_runner_ = nullptr;
 
-  Optional<Future<FVoid>> future_;
+  std::optional<Future<FVoid>> future_;
   FlatSet<PlatformHandle> interested_;
   FlatSet<PlatformHandle> ready_;
   PollContext context_{&interested_, &ready_};
diff --git a/src/base/threading/spawn_unittest.cc b/src/base/threading/spawn_unittest.cc
index 1324b18..a62ae83 100644
--- a/src/base/threading/spawn_unittest.cc
+++ b/src/base/threading/spawn_unittest.cc
@@ -16,8 +16,9 @@
 
 #include "perfetto/ext/base/threading/spawn.h"
 
+#include <optional>
+
 #include "perfetto/ext/base/event_fd.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/thread_task_runner.h"
 #include "perfetto/ext/base/threading/future.h"
 #include "perfetto/ext/base/threading/poll.h"
@@ -65,10 +66,10 @@
       });
 
   task_runner.RunUntilIdle();
-  ASSERT_EQ(res.channel()->ReadNonBlocking().item, base::nullopt);
+  ASSERT_EQ(res.channel()->ReadNonBlocking().item, std::nullopt);
 
   task_runner.RunUntilIdle();
-  ASSERT_EQ(res.channel()->ReadNonBlocking().item, base::nullopt);
+  ASSERT_EQ(res.channel()->ReadNonBlocking().item, std::nullopt);
 
   fd.Notify();
   task_runner.RunUntilIdle();
@@ -108,7 +109,7 @@
       });
 
   task_runner.RunUntilIdle();
-  ASSERT_EQ(res.channel()->ReadNonBlocking().item, base::nullopt);
+  ASSERT_EQ(res.channel()->ReadNonBlocking().item, std::nullopt);
 
   fd.Notify();
   task_runner.RunUntilIdle();
@@ -118,7 +119,7 @@
   ASSERT_FALSE(read.is_closed);
 
   task_runner.RunUntilIdle();
-  ASSERT_EQ(res.channel()->ReadNonBlocking().item, base::nullopt);
+  ASSERT_EQ(res.channel()->ReadNonBlocking().item, std::nullopt);
 
   fd.Notify();
   task_runner.RunUntilIdle();
diff --git a/src/base/threading/util_unittest.cc b/src/base/threading/util_unittest.cc
index 887802c..504435b 100644
--- a/src/base/threading/util_unittest.cc
+++ b/src/base/threading/util_unittest.cc
@@ -16,11 +16,12 @@
 
 #include "perfetto/ext/base/threading/util.h"
 
+#include <optional>
+
 #include "perfetto/base/flat_set.h"
 #include "perfetto/base/platform_handle.h"
 #include "perfetto/base/time.h"
 #include "perfetto/ext/base/event_fd.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/threading/channel.h"
 #include "perfetto/ext/base/threading/poll.h"
 #include "perfetto/ext/base/threading/stream.h"
@@ -45,7 +46,7 @@
   return res.item();
 }
 
-base::Optional<int> WaitForStreamReady(
+std::optional<int> WaitForStreamReady(
     base::Stream<int>& stream,
     base::FlatSet<base::PlatformHandle>& interested,
     PollContext& ctx) {
@@ -55,7 +56,7 @@
     base::BlockUntilReadableFd(*interested.begin());
     interested = {};
   }
-  return res.IsDone() ? base::nullopt : base::make_optional(res.item());
+  return res.IsDone() ? std::nullopt : std::make_optional(res.item());
 }
 
 TEST(UtilUnittest, BlockUntilReadableFd) {
@@ -113,7 +114,7 @@
   interested = {};
 
   ASSERT_EQ(channel.ReadNonBlocking().item, 1);
-  ASSERT_EQ(channel.ReadNonBlocking().item, base::nullopt);
+  ASSERT_EQ(channel.ReadNonBlocking().item, std::nullopt);
 
   ASSERT_FALSE(future.Poll(&ctx).IsPending());
   ASSERT_EQ(channel.ReadNonBlocking().item, 3);
@@ -127,11 +128,11 @@
   base::ThreadPool pool(1);
   base::Stream<int> stream =
       base::RunOnThreadPool<int>(&pool, [counter = 0]() mutable {
-        return counter == 2 ? base::nullopt : base::make_optional(counter++);
+        return counter == 2 ? std::nullopt : std::make_optional(counter++);
       });
   ASSERT_EQ(WaitForStreamReady(stream, interested, ctx), 0);
   ASSERT_EQ(WaitForStreamReady(stream, interested, ctx), 1);
-  ASSERT_EQ(WaitForStreamReady(stream, interested, ctx), base::nullopt);
+  ASSERT_EQ(WaitForStreamReady(stream, interested, ctx), std::nullopt);
 }
 
 TEST(UtilUnittest, RunOnceOnThreadPool) {
diff --git a/src/base/uuid_unittest.cc b/src/base/uuid_unittest.cc
index 9f5e52f..6cd22c9 100644
--- a/src/base/uuid_unittest.cc
+++ b/src/base/uuid_unittest.cc
@@ -16,9 +16,9 @@
 
 #include "perfetto/ext/base/uuid.h"
 
-#include "test/gtest_and_gmock.h"
+#include <optional>
 
-#include "perfetto/ext/base/optional.h"
+#include "test/gtest_and_gmock.h"
 
 namespace perfetto {
 namespace base {
diff --git a/src/kernel_utils/syscall_table.cc b/src/kernel_utils/syscall_table.cc
index ee7cccd..d5a6e23 100644
--- a/src/kernel_utils/syscall_table.cc
+++ b/src/kernel_utils/syscall_table.cc
@@ -98,13 +98,13 @@
   return SyscallTable(arch);
 }
 
-base::Optional<size_t> SyscallTable::GetByName(const std::string& name) const {
+std::optional<size_t> SyscallTable::GetByName(const std::string& name) const {
   for (size_t i = 0; i < syscall_count_; i++) {
     if (name == syscall_table_[i]) {
       return i;
     }
   }
-  return base::nullopt;
+  return std::nullopt;
 }
 
 const char* SyscallTable::GetById(size_t id) const {
diff --git a/src/kernel_utils/syscall_table.h b/src/kernel_utils/syscall_table.h
index 3e88e0a..c2ec4a5 100644
--- a/src/kernel_utils/syscall_table.h
+++ b/src/kernel_utils/syscall_table.h
@@ -18,9 +18,9 @@
 #define SRC_KERNEL_UTILS_SYSCALL_TABLE_H_
 
 #include <memory>
+#include <optional>
 #include <string>
 
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_view.h"
 
 namespace perfetto {
@@ -52,8 +52,8 @@
   static SyscallTable FromCurrentArch();
 
   // Returns the syscall id for the syscall with the given name. If the syscall
-  // is not found, returns nullopt.
-  base::Optional<size_t> GetByName(const std::string& name) const;
+  // is not found, returns std::nullopt.
+  std::optional<size_t> GetByName(const std::string& name) const;
 
   // Returns the syscall name for the syscall with the given id. If the syscall
   // is not found, returns nullptr.
diff --git a/src/perfetto_cmd/pbtxt_to_pb.cc b/src/perfetto_cmd/pbtxt_to_pb.cc
index 1904725..1f11a21 100644
--- a/src/perfetto_cmd/pbtxt_to_pb.cc
+++ b/src/perfetto_cmd/pbtxt_to_pb.cc
@@ -14,19 +14,18 @@
  * limitations under the License.
  */
 
-#include <ctype.h>
+#include "src/perfetto_cmd/pbtxt_to_pb.h"
 
+#include <ctype.h>
 #include <limits>
 #include <map>
+#include <optional>
 #include <set>
 #include <stack>
 #include <string>
 
-#include "src/perfetto_cmd/pbtxt_to_pb.h"
-
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/file_utils.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/ext/base/utils.h"
@@ -442,7 +441,7 @@
   template <typename T>
   void FixedFloatField(const FieldDescriptorProto* field, Token t) {
     uint32_t field_id = static_cast<uint32_t>(field->number());
-    base::Optional<double> opt_n = base::StringToDouble(t.ToStdString());
+    std::optional<double> opt_n = base::StringToDouble(t.ToStdString());
     msg()->AppendFixed<T>(field_id, static_cast<T>(opt_n.value_or(0l)));
   }
 
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc
index a0234b8..4a2b081 100644
--- a/src/perfetto_cmd/perfetto_cmd.cc
+++ b/src/perfetto_cmd/perfetto_cmd.cc
@@ -155,7 +155,7 @@
 #endif  // PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
 }
 
-base::Optional<PerfettoStatsdAtom> ConvertRateLimiterResponseToAtom(
+std::optional<PerfettoStatsdAtom> ConvertRateLimiterResponseToAtom(
     RateLimiter::ShouldTraceResponse resp) {
   switch (resp) {
     case RateLimiter::kNotAllowedOnUserBuild:
@@ -167,7 +167,7 @@
     case RateLimiter::kHitUploadLimit:
       return PerfettoStatsdAtom::kCmdHitUploadLimit;
     case RateLimiter::kOkToTrace:
-      return base::nullopt;
+      return std::nullopt;
   }
   PERFETTO_FATAL("For GCC");
 }
@@ -270,8 +270,8 @@
           argv0);
 }
 
-base::Optional<int> PerfettoCmd::ParseCmdlineAndMaybeDaemonize(int argc,
-                                                               char** argv) {
+std::optional<int> PerfettoCmd::ParseCmdlineAndMaybeDaemonize(int argc,
+                                                              char** argv) {
 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
   umask(0000);  // make sure that file creation is not affected by umask.
 #endif
@@ -831,8 +831,8 @@
     background_wait_pipe_.rd.reset();
   }
 
-  return base::nullopt;  // Continues in ConnectToServiceRunAndMaybeNotify()
-                         // below.
+  return std::nullopt;  // Continues in ConnectToServiceRunAndMaybeNotify()
+                        // below.
 }
 
 void PerfettoCmd::NotifyBgProcessPipe(BgProcessStatus status) {
diff --git a/src/perfetto_cmd/perfetto_cmd.h b/src/perfetto_cmd/perfetto_cmd.h
index bd4a8c4..9504ca5 100644
--- a/src/perfetto_cmd/perfetto_cmd.h
+++ b/src/perfetto_cmd/perfetto_cmd.h
@@ -22,10 +22,10 @@
 #include <vector>
 
 #include <time.h>
+#include <optional>
 
 #include "perfetto/base/build_config.h"
 #include "perfetto/ext/base/event_fd.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/pipe.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/unix_task_runner.h"
@@ -52,10 +52,10 @@
   // with traced. This is to allow tools like tracebox to avoid spawning the
   // service for no reason if the cmdline parsing fails.
   // Return value:
-  //   nullopt: no error, the caller should call
+  //   std::nullopt: no error, the caller should call
   //   ConnectToServiceRunAndMaybeNotify.
   //   0-N: the caller should exit() with the given exit code.
-  base::Optional<int> ParseCmdlineAndMaybeDaemonize(int argc, char** argv);
+  std::optional<int> ParseCmdlineAndMaybeDaemonize(int argc, char** argv);
   int ConnectToServiceRunAndMaybeNotify();
 
   // perfetto::Consumer implementation.
@@ -157,7 +157,7 @@
   bool upload_flag_ = false;
   bool connected_ = false;
   std::string uuid_;
-  base::Optional<TracingSessionID> clone_tsid_{};
+  std::optional<TracingSessionID> clone_tsid_{};
 
   // How long we expect to trace for or 0 if the trace is indefinite.
   uint32_t expected_duration_ms_ = 0;
diff --git a/src/profiling/common/proc_cmdline.cc b/src/profiling/common/proc_cmdline.cc
index a6f7d93..d76ebfb 100644
--- a/src/profiling/common/proc_cmdline.cc
+++ b/src/profiling/common/proc_cmdline.cc
@@ -49,7 +49,7 @@
 // Keep them as STL-free as possible to allow for both implementations to be
 // close to verbatim copies.
 
-// TODO(rsavitski): consider changing to Optional<> return type.
+// TODO(rsavitski): consider changing to std::optional<> return type.
 bool ReadProcCmdlineForPID(pid_t pid, std::string* cmdline_out) {
   std::string filename = "/proc/" + std::to_string(pid) + "/cmdline";
   base::ScopedFile fd(base::OpenFile(filename, O_RDONLY));
diff --git a/src/profiling/common/proc_utils.cc b/src/profiling/common/proc_utils.cc
index 4188b2a..83fbb4a 100644
--- a/src/profiling/common/proc_utils.cc
+++ b/src/profiling/common/proc_utils.cc
@@ -20,9 +20,9 @@
 #include <unistd.h>
 
 #include <cinttypes>
+#include <optional>
 
 #include "perfetto/ext/base/file_utils.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "src/profiling/common/proc_cmdline.h"
 
@@ -30,8 +30,8 @@
 namespace profiling {
 namespace {
 
-base::Optional<uint32_t> ParseProcStatusSize(const std::string& status,
-                                             const std::string& key) {
+std::optional<uint32_t> ParseProcStatusSize(const std::string& status,
+                                            const std::string& key) {
   auto entry_idx = status.find(key);
   if (entry_idx == std::string::npos)
     return {};
@@ -47,32 +47,32 @@
 }
 }  // namespace
 
-base::Optional<std::string> ReadStatus(pid_t pid) {
+std::optional<std::string> ReadStatus(pid_t pid) {
   std::string path = "/proc/" + std::to_string(pid) + "/status";
   std::string status;
   bool read_proc = base::ReadFile(path, &status);
   if (!read_proc) {
     PERFETTO_ELOG("Failed to read %s", path.c_str());
-    return base::nullopt;
+    return std::nullopt;
   }
-  return base::Optional<std::string>(status);
+  return std::optional<std::string>(status);
 }
 
-base::Optional<uint32_t> GetRssAnonAndSwap(const std::string& status) {
+std::optional<uint32_t> GetRssAnonAndSwap(const std::string& status) {
   auto anon_rss = ParseProcStatusSize(status, "RssAnon:");
   auto swap = ParseProcStatusSize(status, "VmSwap:");
   if (anon_rss.has_value() && swap.has_value()) {
     return *anon_rss + *swap;
   }
-  return base::nullopt;
+  return std::nullopt;
 }
 
 void RemoveUnderAnonThreshold(uint32_t min_size_kb, std::set<pid_t>* pids) {
   for (auto it = pids->begin(); it != pids->end();) {
     const pid_t pid = *it;
 
-    base::Optional<std::string> status = ReadStatus(pid);
-    base::Optional<uint32_t> rss_and_swap;
+    std::optional<std::string> status = ReadStatus(pid);
+    std::optional<uint32_t> rss_and_swap;
     if (status)
       rss_and_swap = GetRssAnonAndSwap(*status);
 
@@ -87,10 +87,10 @@
   }
 }
 
-base::Optional<Uids> GetUids(const std::string& status) {
+std::optional<Uids> GetUids(const std::string& status) {
   auto entry_idx = status.find("Uid:");
   if (entry_idx == std::string::npos)
-    return base::nullopt;
+    return std::nullopt;
 
   Uids uids;
   const char* str = &status[entry_idx + 4];
@@ -98,22 +98,22 @@
 
   uids.real = strtoull(str, &endptr, 10);
   if (*endptr != ' ' && *endptr != '\t')
-    return base::nullopt;
+    return std::nullopt;
 
   str = endptr;
   uids.effective = strtoull(str, &endptr, 10);
   if (*endptr != ' ' && *endptr != '\t')
-    return base::nullopt;
+    return std::nullopt;
 
   str = endptr;
   uids.saved_set = strtoull(str, &endptr, 10);
   if (*endptr != ' ' && *endptr != '\t')
-    return base::nullopt;
+    return std::nullopt;
 
   str = endptr;
   uids.filesystem = strtoull(str, &endptr, 10);
   if (*endptr != '\n' && *endptr != '\0')
-    return base::nullopt;
+    return std::nullopt;
   return uids;
 }
 
@@ -146,7 +146,7 @@
   return first_arg - start;
 }
 
-base::Optional<std::vector<std::string>> NormalizeCmdlines(
+std::optional<std::vector<std::string>> NormalizeCmdlines(
     const std::vector<std::string>& cmdlines) {
   std::vector<std::string> normalized_cmdlines;
   normalized_cmdlines.reserve(cmdlines.size());
@@ -160,11 +160,11 @@
     if (size == -1) {
       PERFETTO_PLOG("Failed to normalize cmdline %s. Stopping the parse.",
                     cmdlines[i].c_str());
-      return base::nullopt;
+      return std::nullopt;
     }
     normalized_cmdlines.emplace_back(cmdline_cstr, static_cast<size_t>(size));
   }
-  return base::make_optional(normalized_cmdlines);
+  return std::make_optional(normalized_cmdlines);
 }
 
 // This is mostly the same as GetHeapprofdProgramProperty in
diff --git a/src/profiling/common/proc_utils.h b/src/profiling/common/proc_utils.h
index ccbf633..4792feb 100644
--- a/src/profiling/common/proc_utils.h
+++ b/src/profiling/common/proc_utils.h
@@ -20,10 +20,10 @@
 #include <sys/types.h>
 
 #include <cinttypes>
+#include <optional>
 #include <set>
 #include <vector>
 
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/scoped_file.h"
 
 namespace perfetto {
@@ -53,13 +53,13 @@
   }
 }
 
-base::Optional<std::string> ReadStatus(pid_t pid);
-base::Optional<uint32_t> GetRssAnonAndSwap(const std::string&);
+std::optional<std::string> ReadStatus(pid_t pid);
+std::optional<uint32_t> GetRssAnonAndSwap(const std::string&);
 // Filters the list of pids (in-place), keeping only the
 // entries satisfying the minimum size criteria for anonymous memory.
 void RemoveUnderAnonThreshold(uint32_t min_size_kb, std::set<pid_t>* pids);
 
-base::Optional<Uids> GetUids(const std::string&);
+std::optional<Uids> GetUids(const std::string&);
 
 void FindAllProfilablePids(std::set<pid_t>* pids);
 
@@ -68,7 +68,7 @@
 // implementations are placed in the "glob_aware" namespace here, until we
 // migrate to one implementation for all profilers.
 ssize_t NormalizeCmdLine(char** cmdline_ptr, size_t size);
-base::Optional<std::vector<std::string>> NormalizeCmdlines(
+std::optional<std::vector<std::string>> NormalizeCmdlines(
     const std::vector<std::string>& cmdlines);
 void FindPidsForCmdlines(const std::vector<std::string>& cmdlines,
                          std::set<pid_t>* pids);
diff --git a/src/profiling/common/proc_utils_unittest.cc b/src/profiling/common/proc_utils_unittest.cc
index 36a7720..8ce34e4 100644
--- a/src/profiling/common/proc_utils_unittest.cc
+++ b/src/profiling/common/proc_utils_unittest.cc
@@ -15,7 +15,8 @@
  */
 
 #include "src/profiling/common/proc_utils.h"
-#include "perfetto/ext/base/optional.h"
+
+#include <optional>
 
 #include "perfetto/ext/base/utils.h"
 #include "test/gtest_and_gmock.h"
@@ -131,9 +132,9 @@
 }
 
 TEST(ProcUtilsTest, GetRssAnonAndSwapInvalidInput) {
-  EXPECT_EQ(GetRssAnonAndSwap(""), base::nullopt);
-  EXPECT_EQ(GetRssAnonAndSwap("RssAnon: 10000 kB"), base::nullopt);
-  EXPECT_EQ(GetRssAnonAndSwap("VmSwap: 10000"), base::nullopt);
+  EXPECT_EQ(GetRssAnonAndSwap(""), std::nullopt);
+  EXPECT_EQ(GetRssAnonAndSwap("RssAnon: 10000 kB"), std::nullopt);
+  EXPECT_EQ(GetRssAnonAndSwap("VmSwap: 10000"), std::nullopt);
 }
 
 TEST(ProcUtilsTest, GetUids) {
@@ -141,7 +142,7 @@
       "Name: foo\nRssAnon:  10000 kB\nVmSwap:\t10000 kB\n"
       "Uid: 1 2 3 4\n";
   auto uids = GetUids(status);
-  ASSERT_NE(uids, base::nullopt);
+  ASSERT_NE(uids, std::nullopt);
   EXPECT_EQ(uids->real, 1u);
   EXPECT_EQ(uids->effective, 2u);
   EXPECT_EQ(uids->saved_set, 3u);
@@ -153,7 +154,7 @@
       "Name: foo\nRssAnon:  10000 kB\nVmSwap:\t10000 kB\n"
       "Uid: 1a 2 3 4\n";
   auto uids = GetUids(status);
-  EXPECT_EQ(uids, base::nullopt);
+  EXPECT_EQ(uids, std::nullopt);
 }
 
 TEST(ProcUtilsTest, GetUidsInvalidTooFew) {
@@ -161,7 +162,7 @@
       "Name: foo\nRssAnon:  10000 kB\nVmSwap:\t10000 kB\n"
       "Uid: 1 2 3\n";
   auto uids = GetUids(status);
-  EXPECT_EQ(uids, base::nullopt);
+  EXPECT_EQ(uids, std::nullopt);
 }
 
 }  // namespace
diff --git a/src/profiling/common/producer_support.cc b/src/profiling/common/producer_support.cc
index a160149..5303658 100644
--- a/src/profiling/common/producer_support.cc
+++ b/src/profiling/common/producer_support.cc
@@ -16,9 +16,10 @@
 
 #include "src/profiling/common/producer_support.h"
 
+#include <optional>
+
 #include "perfetto/ext/base/android_utils.h"
 #include "perfetto/ext/base/file_utils.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_splitter.h"
 #include "perfetto/tracing/core/data_source_config.h"
 #include "src/traced/probes/packages_list/packages_list_parser.h"
@@ -27,26 +28,26 @@
 namespace profiling {
 
 namespace {
-base::Optional<Package> FindInPackagesList(
+std::optional<Package> FindInPackagesList(
     uint64_t lookup_uid,
     const std::string& packages_list_path) {
   std::string content;
   if (!base::ReadFile(packages_list_path, &content)) {
     PERFETTO_ELOG("Failed to read %s", packages_list_path.c_str());
-    return base::nullopt;
+    return std::nullopt;
   }
   for (base::StringSplitter ss(std::move(content), '\n'); ss.Next();) {
     Package pkg;
     if (!ReadPackagesListLine(ss.cur_token(), &pkg)) {
       PERFETTO_ELOG("Failed to parse packages.list");
-      return base::nullopt;
+      return std::nullopt;
     }
 
     if (pkg.uid == lookup_uid) {
       return std::move(pkg);  // -Wreturn-std-move-in-c++11
     }
   }
-  return base::nullopt;
+  return std::nullopt;
 }
 
 bool AllPackagesProfileableByTrustedInitiator(
@@ -156,7 +157,7 @@
     return false;
   }
 
-  base::Optional<Package> pkg =
+  std::optional<Package> pkg =
       FindInPackagesList(uid_for_lookup, packages_list_path);
 
   if (!pkg)
diff --git a/src/profiling/common/profiler_guardrails.cc b/src/profiling/common/profiler_guardrails.cc
index a7f5dad..38ff6ed 100644
--- a/src/profiling/common/profiler_guardrails.cc
+++ b/src/profiling/common/profiler_guardrails.cc
@@ -17,29 +17,29 @@
 #include "src/profiling/common/profiler_guardrails.h"
 
 #include <unistd.h>
-
 #include <algorithm>
+#include <optional>
+
 #include "perfetto/ext/base/file_utils.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/watchdog_posix.h"
 
 namespace perfetto {
 namespace profiling {
 
-base::Optional<uint64_t> GetCputimeSecForCurrentProcess() {
+std::optional<uint64_t> GetCputimeSecForCurrentProcess() {
   return GetCputimeSecForCurrentProcess(
       base::OpenFile("/proc/self/stat", O_RDONLY));
 }
 
-base::Optional<uint64_t> GetCputimeSecForCurrentProcess(
+std::optional<uint64_t> GetCputimeSecForCurrentProcess(
     base::ScopedFile stat_fd) {
   if (!stat_fd)
-    return base::nullopt;
+    return std::nullopt;
   base::ProcStat stat;
   if (!ReadProcStat(stat_fd.get(), &stat)) {
     PERFETTO_ELOG("Failed to read stat file to enforce guardrails.");
-    return base::nullopt;
+    return std::nullopt;
   }
   return (stat.utime + stat.stime) /
          static_cast<unsigned long>(sysconf(_SC_CLK_TCK));
diff --git a/src/profiling/common/profiler_guardrails.h b/src/profiling/common/profiler_guardrails.h
index e9e9d47..4fc723f 100644
--- a/src/profiling/common/profiler_guardrails.h
+++ b/src/profiling/common/profiler_guardrails.h
@@ -21,9 +21,9 @@
 #include <unistd.h>
 
 #include <cinttypes>
+#include <optional>
 
 #include "perfetto/ext/base/file_utils.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/tracing/core/basic_types.h"
 #include "src/profiling/common/proc_utils.h"
@@ -31,14 +31,14 @@
 namespace perfetto {
 namespace profiling {
 
-base::Optional<uint64_t> GetCputimeSecForCurrentProcess();
+std::optional<uint64_t> GetCputimeSecForCurrentProcess();
 // For testing.
-base::Optional<uint64_t> GetCputimeSecForCurrentProcess(
+std::optional<uint64_t> GetCputimeSecForCurrentProcess(
     base::ScopedFile stat_fd);
 
 struct GuardrailConfig {
   uint64_t cpu_guardrail_sec = 0;
-  base::Optional<uint64_t> cpu_start_secs;
+  std::optional<uint64_t> cpu_start_secs;
   uint32_t memory_guardrail_kb = 0;
 };
 
@@ -51,7 +51,7 @@
   bool IsOverCpuThreshold(const GuardrailConfig& ds);
 
  private:
-  base::Optional<uint64_t> opt_cputime_sec_;
+  std::optional<uint64_t> opt_cputime_sec_;
 };
 
 class ProfilerMemoryGuardrails {
@@ -63,7 +63,7 @@
   bool IsOverMemoryThreshold(const GuardrailConfig& ds);
 
  private:
-  base::Optional<uint32_t> anon_and_swap_;
+  std::optional<uint32_t> anon_and_swap_;
 };
 
 }  // namespace profiling
diff --git a/src/profiling/common/profiler_guardrails_unittest.cc b/src/profiling/common/profiler_guardrails_unittest.cc
index ca9d1b2..1368cbe 100644
--- a/src/profiling/common/profiler_guardrails_unittest.cc
+++ b/src/profiling/common/profiler_guardrails_unittest.cc
@@ -20,9 +20,9 @@
 
 #include <cinttypes>
 #include <map>
+#include <optional>
 
 #include "perfetto/ext/base/file_utils.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/temp_file.h"
 #include "test/gtest_and_gmock.h"
 
diff --git a/src/profiling/deobfuscator.cc b/src/profiling/deobfuscator.cc
index 378677e..fa32706 100644
--- a/src/profiling/deobfuscator.cc
+++ b/src/profiling/deobfuscator.cc
@@ -16,12 +16,12 @@
 
 #include "src/profiling/deobfuscator.h"
 
+#include <optional>
 #include "perfetto/base/status.h"
 #include "perfetto/ext/base/file_utils.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/string_splitter.h"
 
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/protozero/scattered_heap_buffer.h"
 #include "protos/perfetto/trace/profiling/deobfuscation.pbzero.h"
 #include "protos/perfetto/trace/trace.pbzero.h"
@@ -36,39 +36,39 @@
   std::string deobfuscated_name;
 };
 
-base::Optional<ProguardClass> ParseClass(std::string line) {
+std::optional<ProguardClass> ParseClass(std::string line) {
   base::StringSplitter ss(std::move(line), ' ');
 
   if (!ss.Next()) {
     PERFETTO_ELOG("Missing deobfuscated name.");
-    return base::nullopt;
+    return std::nullopt;
   }
   std::string deobfuscated_name(ss.cur_token(), ss.cur_token_size());
 
   if (!ss.Next() || ss.cur_token_size() != 2 ||
       strncmp("->", ss.cur_token(), 2) != 0) {
     PERFETTO_ELOG("Missing ->");
-    return base::nullopt;
+    return std::nullopt;
   }
 
   if (!ss.Next()) {
     PERFETTO_ELOG("Missing obfuscated name.");
-    return base::nullopt;
+    return std::nullopt;
   }
   std::string obfuscated_name(ss.cur_token(), ss.cur_token_size());
   if (obfuscated_name.empty()) {
     PERFETTO_ELOG("Empty obfuscated name.");
-    return base::nullopt;
+    return std::nullopt;
   }
   if (obfuscated_name.back() != ':') {
     PERFETTO_ELOG("Expected colon.");
-    return base::nullopt;
+    return std::nullopt;
   }
 
   obfuscated_name.resize(obfuscated_name.size() - 1);
   if (ss.Next()) {
     PERFETTO_ELOG("Unexpected data.");
-    return base::nullopt;
+    return std::nullopt;
   }
   return ProguardClass{std::move(obfuscated_name),
                        std::move(deobfuscated_name)};
@@ -85,36 +85,36 @@
   std::string deobfuscated_name;
 };
 
-base::Optional<ProguardMember> ParseMember(std::string line) {
+std::optional<ProguardMember> ParseMember(std::string line) {
   base::StringSplitter ss(std::move(line), ' ');
 
   if (!ss.Next()) {
     PERFETTO_ELOG("Missing type name.");
-    return base::nullopt;
+    return std::nullopt;
   }
   std::string type_name(ss.cur_token(), ss.cur_token_size());
 
   if (!ss.Next()) {
     PERFETTO_ELOG("Missing deobfuscated name.");
-    return base::nullopt;
+    return std::nullopt;
   }
   std::string deobfuscated_name(ss.cur_token(), ss.cur_token_size());
 
   if (!ss.Next() || ss.cur_token_size() != 2 ||
       strncmp("->", ss.cur_token(), 2) != 0) {
     PERFETTO_ELOG("Missing ->");
-    return base::nullopt;
+    return std::nullopt;
   }
 
   if (!ss.Next()) {
     PERFETTO_ELOG("Missing obfuscated name.");
-    return base::nullopt;
+    return std::nullopt;
   }
   std::string obfuscated_name(ss.cur_token(), ss.cur_token_size());
 
   if (ss.Next()) {
     PERFETTO_ELOG("Unexpected data.");
-    return base::nullopt;
+    return std::nullopt;
   }
 
   ProguardMemberType member_type;
diff --git a/src/profiling/memory/client.cc b/src/profiling/memory/client.cc
index 79e1a77..478a900 100644
--- a/src/profiling/memory/client.cc
+++ b/src/profiling/memory/client.cc
@@ -140,21 +140,21 @@
 }
 
 // static
-base::Optional<base::UnixSocketRaw> Client::ConnectToHeapprofd(
+std::optional<base::UnixSocketRaw> Client::ConnectToHeapprofd(
     const std::string& sock_name) {
   auto sock = base::UnixSocketRaw::CreateMayFail(base::SockFamily::kUnix,
                                                  base::SockType::kStream);
   if (!sock || !sock.Connect(sock_name)) {
     PERFETTO_PLOG("Failed to connect to %s", sock_name.c_str());
-    return base::nullopt;
+    return std::nullopt;
   }
   if (!sock.SetTxTimeout(kClientSockTimeoutMs)) {
     PERFETTO_PLOG("Failed to set send timeout for %s", sock_name.c_str());
-    return base::nullopt;
+    return std::nullopt;
   }
   if (!sock.SetRxTimeout(kClientSockTimeoutMs)) {
     PERFETTO_PLOG("Failed to set receive timeout for %s", sock_name.c_str());
-    return base::nullopt;
+    return std::nullopt;
   }
   return std::move(sock);
 }
diff --git a/src/profiling/memory/client.h b/src/profiling/memory/client.h
index 8b5f341..0e209fb 100644
--- a/src/profiling/memory/client.h
+++ b/src/profiling/memory/client.h
@@ -74,7 +74,7 @@
       base::UnixSocketRaw sock,
       UnhookedAllocator<Client> unhooked_allocator);
 
-  static base::Optional<base::UnixSocketRaw> ConnectToHeapprofd(
+  static std::optional<base::UnixSocketRaw> ConnectToHeapprofd(
       const std::string& sock_name);
 
   bool RecordMalloc(uint32_t heap_id,
diff --git a/src/profiling/memory/client_api_factory_android.cc b/src/profiling/memory/client_api_factory_android.cc
index 16c3a25..9c7b63c 100644
--- a/src/profiling/memory/client_api_factory_android.cc
+++ b/src/profiling/memory/client_api_factory_android.cc
@@ -23,9 +23,9 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <unistd.h>
+#include <optional>
 
 #include "perfetto/base/build_config.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/unix_socket.h"
 #include "src/profiling/memory/client.h"
 
@@ -43,7 +43,7 @@
   PERFETTO_LOG("Constructing client for central daemon.");
   using perfetto::profiling::Client;
 
-  perfetto::base::Optional<perfetto::base::UnixSocketRaw> sock =
+  std::optional<perfetto::base::UnixSocketRaw> sock =
       Client::ConnectToHeapprofd(perfetto::profiling::kHeapprofdSocketFile);
   if (!sock) {
     PERFETTO_ELOG("Failed to connect to %s. This is benign on user builds.",
diff --git a/src/profiling/memory/heapprofd_end_to_end_test.cc b/src/profiling/memory/heapprofd_end_to_end_test.cc
index de9f40e..66acd81 100644
--- a/src/profiling/memory/heapprofd_end_to_end_test.cc
+++ b/src/profiling/memory/heapprofd_end_to_end_test.cc
@@ -25,11 +25,11 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <unistd.h>
+#include <optional>
 
 #include "perfetto/base/build_config.h"
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/file_utils.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/pipe.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/subprocess.h"
@@ -97,7 +97,7 @@
   int64_t cumulative_alloc_count;
   int64_t alloc_size;
   int64_t cumulative_alloc_size;
-  base::Optional<int64_t> parent_id;
+  std::optional<int64_t> parent_id;
 };
 
 std::vector<FlamegraphNode> GetFlamegraph(trace_processor::TraceProcessor* tp) {
@@ -116,8 +116,8 @@
         it.Get(8).AsLong(),
         it.Get(9).AsLong(),
         it.Get(10).AsLong(),
-        it.Get(11).is_null() ? base::nullopt
-                             : base::Optional<int64_t>(it.Get(11).AsLong()),
+        it.Get(11).is_null() ? std::nullopt
+                             : std::optional<int64_t>(it.Get(11).AsLong()),
     });
   }
   PERFETTO_CHECK(it.Status().ok());
diff --git a/src/profiling/memory/heapprofd_producer.cc b/src/profiling/memory/heapprofd_producer.cc
index 2d35965..7d0cfe8 100644
--- a/src/profiling/memory/heapprofd_producer.cc
+++ b/src/profiling/memory/heapprofd_producer.cc
@@ -24,12 +24,12 @@
 #include <algorithm>
 #include <cinttypes>
 #include <functional>
+#include <optional>
 #include <string>
 
 #include "perfetto/base/compiler.h"
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/file_utils.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_splitter.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/thread_task_runner.h"
@@ -390,7 +390,7 @@
     return;
   }
 
-  base::Optional<std::vector<std::string>> normalized_cmdlines =
+  std::optional<std::vector<std::string>> normalized_cmdlines =
       NormalizeCmdlines(heapprofd_config.process_cmdline());
   if (!normalized_cmdlines.has_value()) {
     PERFETTO_ELOG("Rejecting data source due to invalid cmdline in config.");
@@ -416,7 +416,7 @@
     }
   }
 
-  base::Optional<uint64_t> start_cputime_sec;
+  std::optional<uint64_t> start_cputime_sec;
   if (heapprofd_config.max_heapprofd_cpu_secs() > 0) {
     start_cputime_sec = GetCputimeSecForCurrentProcess();
 
diff --git a/src/profiling/memory/heapprofd_producer.h b/src/profiling/memory/heapprofd_producer.h
index 92f830a..ac18b54 100644
--- a/src/profiling/memory/heapprofd_producer.h
+++ b/src/profiling/memory/heapprofd_producer.h
@@ -21,10 +21,10 @@
 #include <cinttypes>
 #include <functional>
 #include <map>
+#include <optional>
 #include <vector>
 
 #include "perfetto/base/task_runner.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/unix_socket.h"
 #include "perfetto/ext/base/unix_task_runner.h"
 #include "perfetto/ext/tracing/core/basic_types.h"
@@ -317,7 +317,7 @@
 
   // Specific to mode_ == kChild
   Process target_process_{base::kInvalidPid, ""};
-  base::Optional<std::function<void()>> data_source_callback_;
+  std::optional<std::function<void()>> data_source_callback_;
 
   SocketDelegate socket_delegate_;
 
diff --git a/src/profiling/memory/heapprofd_producer_integrationtest.cc b/src/profiling/memory/heapprofd_producer_integrationtest.cc
index 077a539..d0ea2c4 100644
--- a/src/profiling/memory/heapprofd_producer_integrationtest.cc
+++ b/src/profiling/memory/heapprofd_producer_integrationtest.cc
@@ -181,7 +181,7 @@
   tmpdir_.TrackFile("producer.sock");
   tmpdir_.TrackFile("consumer.sock");
 
-  base::Optional<TracingServiceThread> tracing_service;
+  std::optional<TracingServiceThread> tracing_service;
   tracing_service.emplace(tmpdir_.AbsolutePath("producer.sock"),
                           tmpdir_.AbsolutePath("consumer.sock"));
 
@@ -193,7 +193,7 @@
       StartHeapprofdTrace(tmpdir_.AbsolutePath("consumer.sock"), &task_runner);
   ASSERT_THAT(consumer, NotNull());
 
-  base::Optional<base::UnixSocketRaw> client_sock =
+  std::optional<base::UnixSocketRaw> client_sock =
       perfetto::profiling::Client::ConnectToHeapprofd(
           tmpdir_.AbsolutePath("heapprofd.sock"));
   ASSERT_TRUE(client_sock.has_value());
diff --git a/src/profiling/memory/java_hprof_producer.cc b/src/profiling/memory/java_hprof_producer.cc
index 81a9c27..01bd01c 100644
--- a/src/profiling/memory/java_hprof_producer.cc
+++ b/src/profiling/memory/java_hprof_producer.cc
@@ -18,8 +18,8 @@
 
 #include <signal.h>
 #include <limits>
+#include <optional>
 
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/tracing/core/trace_writer.h"
 #include "src/profiling/common/proc_cmdline.h"
 #include "src/profiling/common/proc_utils.h"
diff --git a/src/profiling/memory/shared_ring_buffer.cc b/src/profiling/memory/shared_ring_buffer.cc
index 3cc02e2..32d9b3c 100644
--- a/src/profiling/memory/shared_ring_buffer.cc
+++ b/src/profiling/memory/shared_ring_buffer.cc
@@ -186,7 +186,7 @@
   PERFETTO_DCHECK(spinlock.locked());
   Buffer result;
 
-  base::Optional<PointerPositions> opt_pos = GetPointerPositions();
+  std::optional<PointerPositions> opt_pos = GetPointerPositions();
   if (!opt_pos) {
     meta_->stats.num_writes_corrupt++;
     errno = EBADF;
@@ -245,7 +245,7 @@
 }
 
 SharedRingBuffer::Buffer SharedRingBuffer::BeginRead() {
-  base::Optional<PointerPositions> opt_pos = GetPointerPositions();
+  std::optional<PointerPositions> opt_pos = GetPointerPositions();
   if (!opt_pos) {
     meta_->stats.num_reads_corrupt++;
     errno = EBADF;
@@ -322,20 +322,20 @@
 }
 
 // static
-base::Optional<SharedRingBuffer> SharedRingBuffer::Create(size_t size) {
+std::optional<SharedRingBuffer> SharedRingBuffer::Create(size_t size) {
   auto buf = SharedRingBuffer(CreateFlag(), size);
   if (!buf.is_valid())
-    return base::nullopt;
-  return base::make_optional(std::move(buf));
+    return std::nullopt;
+  return std::make_optional(std::move(buf));
 }
 
 // static
-base::Optional<SharedRingBuffer> SharedRingBuffer::Attach(
+std::optional<SharedRingBuffer> SharedRingBuffer::Attach(
     base::ScopedFile mem_fd) {
   auto buf = SharedRingBuffer(AttachFlag(), std::move(mem_fd));
   if (!buf.is_valid())
-    return base::nullopt;
-  return base::make_optional(std::move(buf));
+    return std::nullopt;
+  return std::make_optional(std::move(buf));
 }
 
 }  // namespace profiling
diff --git a/src/profiling/memory/shared_ring_buffer.h b/src/profiling/memory/shared_ring_buffer.h
index 5bdb28e..9bc2e26 100644
--- a/src/profiling/memory/shared_ring_buffer.h
+++ b/src/profiling/memory/shared_ring_buffer.h
@@ -16,8 +16,8 @@
 
 #ifndef SRC_PROFILING_MEMORY_SHARED_RING_BUFFER_H_
 #define SRC_PROFILING_MEMORY_SHARED_RING_BUFFER_H_
+#include <optional>
 
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/unix_socket.h"
 #include "perfetto/ext/base/utils.h"
 #include "src/profiling/memory/scoped_spinlock.h"
@@ -97,8 +97,8 @@
     PERFETTO_CROSS_ABI_ALIGNED(ErrorState) error_state;
   };
 
-  static base::Optional<SharedRingBuffer> Create(size_t);
-  static base::Optional<SharedRingBuffer> Attach(base::ScopedFile);
+  static std::optional<SharedRingBuffer> Create(size_t);
+  static std::optional<SharedRingBuffer> Attach(base::ScopedFile);
 
   ~SharedRingBuffer();
   SharedRingBuffer() = default;
@@ -228,7 +228,7 @@
   void Initialize(base::ScopedFile mem_fd);
   bool IsCorrupt(const PointerPositions& pos);
 
-  inline base::Optional<PointerPositions> GetPointerPositions() {
+  inline std::optional<PointerPositions> GetPointerPositions() {
     PointerPositions pos;
     // We need to acquire load the write_pos to make sure we observe a
     // consistent ring buffer in BeginRead, otherwise it is possible that we
@@ -239,7 +239,7 @@
     pos.write_pos = meta_->write_pos.load(std::memory_order_acquire);
     pos.read_pos = meta_->read_pos.load(std::memory_order_relaxed);
 
-    base::Optional<PointerPositions> result;
+    std::optional<PointerPositions> result;
     if (IsCorrupt(pos))
       return result;
     result = pos;
diff --git a/src/profiling/memory/shared_ring_buffer_unittest.cc b/src/profiling/memory/shared_ring_buffer_unittest.cc
index 7f58b90..ff533f3 100644
--- a/src/profiling/memory/shared_ring_buffer_unittest.cc
+++ b/src/profiling/memory/shared_ring_buffer_unittest.cc
@@ -18,11 +18,11 @@
 
 #include <array>
 #include <mutex>
+#include <optional>
 #include <random>
 #include <thread>
 #include <unordered_map>
 
-#include "perfetto/ext/base/optional.h"
 #include "test/gtest_and_gmock.h"
 
 namespace perfetto {
@@ -147,18 +147,18 @@
 
 TEST(SharedRingBufferTest, ReadShutdown) {
   constexpr auto kBufSize = base::kPageSize * 4;
-  base::Optional<SharedRingBuffer> wr = SharedRingBuffer::Create(kBufSize);
+  std::optional<SharedRingBuffer> wr = SharedRingBuffer::Create(kBufSize);
   ASSERT_TRUE(wr);
   SharedRingBuffer rd =
       *SharedRingBuffer::Attach(base::ScopedFile(dup(wr->fd())));
   auto buf = rd.BeginRead();
-  wr = base::nullopt;
+  wr = std::nullopt;
   rd.EndRead(std::move(buf));
 }
 
 TEST(SharedRingBufferTest, WriteShutdown) {
   constexpr auto kBufSize = base::kPageSize * 4;
-  base::Optional<SharedRingBuffer> rd = SharedRingBuffer::Create(kBufSize);
+  std::optional<SharedRingBuffer> rd = SharedRingBuffer::Create(kBufSize);
   ASSERT_TRUE(rd);
   SharedRingBuffer wr =
       *SharedRingBuffer::Attach(base::ScopedFile(dup(rd->fd())));
@@ -167,21 +167,21 @@
     auto lock = wr.AcquireLock(ScopedSpinlock::Mode::Blocking);
     buf = wr.BeginWrite(lock, 10);
   }
-  rd = base::nullopt;
+  rd = std::nullopt;
   memset(buf.data, 0, buf.size);
   wr.EndWrite(std::move(buf));
 }
 
 TEST(SharedRingBufferTest, SingleThreadSameInstance) {
   constexpr auto kBufSize = base::kPageSize * 4;
-  base::Optional<SharedRingBuffer> buf = SharedRingBuffer::Create(kBufSize);
+  std::optional<SharedRingBuffer> buf = SharedRingBuffer::Create(kBufSize);
   StructuredTest(&*buf, &*buf);
 }
 
 TEST(SharedRingBufferTest, SingleThreadAttach) {
   constexpr auto kBufSize = base::kPageSize * 4;
-  base::Optional<SharedRingBuffer> buf1 = SharedRingBuffer::Create(kBufSize);
-  base::Optional<SharedRingBuffer> buf2 =
+  std::optional<SharedRingBuffer> buf1 = SharedRingBuffer::Create(kBufSize);
+  std::optional<SharedRingBuffer> buf2 =
       SharedRingBuffer::Attach(base::ScopedFile(dup(buf1->fd())));
   StructuredTest(&*buf1, &*buf2);
 }
@@ -256,13 +256,13 @@
 
 TEST(SharedRingBufferTest, InvalidSize) {
   constexpr auto kBufSize = base::kPageSize * 4 + 1;
-  base::Optional<SharedRingBuffer> wr = SharedRingBuffer::Create(kBufSize);
-  EXPECT_EQ(wr, base::nullopt);
+  std::optional<SharedRingBuffer> wr = SharedRingBuffer::Create(kBufSize);
+  EXPECT_EQ(wr, std::nullopt);
 }
 
 TEST(SharedRingBufferTest, EmptyWrite) {
   constexpr auto kBufSize = base::kPageSize * 4;
-  base::Optional<SharedRingBuffer> wr = SharedRingBuffer::Create(kBufSize);
+  std::optional<SharedRingBuffer> wr = SharedRingBuffer::Create(kBufSize);
   ASSERT_TRUE(wr);
   SharedRingBuffer::Buffer buf;
   {
diff --git a/src/profiling/perf/event_config.cc b/src/profiling/perf/event_config.cc
index a89d0c9..25b05c3 100644
--- a/src/profiling/perf/event_config.cc
+++ b/src/profiling/perf/event_config.cc
@@ -20,10 +20,10 @@
 #include <time.h>
 
 #include <unwindstack/Regs.h>
+#include <optional>
 #include <vector>
 
 #include "perfetto/base/flat_set.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/utils.h"
 #include "src/profiling/perf/regs_parsing.h"
 
@@ -56,7 +56,7 @@
 }
 
 // If set, the returned id is guaranteed to be non-zero.
-base::Optional<uint32_t> ParseTracepointAndResolveId(
+std::optional<uint32_t> ParseTracepointAndResolveId(
     const protos::gen::PerfEvents::Tracepoint& tracepoint,
     EventConfig::tracepoint_id_fn_t tracepoint_id_lookup) {
   std::string full_name = tracepoint.name();
@@ -68,7 +68,7 @@
         "Invalid tracepoint format: %s. Should be a full path like "
         "sched:sched_switch or sched/sched_switch.",
         full_name.c_str());
-    return base::nullopt;
+    return std::nullopt;
   }
 
   uint32_t tracepoint_id = tracepoint_id_lookup(tp_group, tp_name);
@@ -77,9 +77,9 @@
         "Failed to resolve tracepoint %s to its id. Check that tracefs is "
         "accessible and the event exists.",
         full_name.c_str());
-    return base::nullopt;
+    return std::nullopt;
   }
-  return base::make_optional(tracepoint_id);
+  return std::make_optional(tracepoint_id);
 }
 
 // |T| is either gen::PerfEventConfig or gen::PerfEventConfig::Scope.
@@ -91,7 +91,7 @@
 template <typename T>
 TargetFilter ParseTargetFilter(
     const T& cfg,
-    base::Optional<ProcessSharding> process_sharding) {
+    std::optional<ProcessSharding> process_sharding) {
   TargetFilter filter;
   for (const auto& str : cfg.target_cmdline()) {
     filter.cmdlines.push_back(str);
@@ -114,22 +114,22 @@
   return (v != 0 && ((v & (v - 1)) == 0));
 }
 
-// returns |base::nullopt| if the input is invalid.
-base::Optional<uint32_t> ChooseActualRingBufferPages(uint32_t config_value) {
+// returns |std::nullopt| if the input is invalid.
+std::optional<uint32_t> ChooseActualRingBufferPages(uint32_t config_value) {
   if (!config_value) {
     static_assert(IsPowerOfTwo(kDefaultDataPagesPerRingBuffer), "");
-    return base::make_optional(kDefaultDataPagesPerRingBuffer);
+    return std::make_optional(kDefaultDataPagesPerRingBuffer);
   }
 
   if (!IsPowerOfTwo(config_value)) {
     PERFETTO_ELOG("kernel buffer size must be a power of two pages");
-    return base::nullopt;
+    return std::nullopt;
   }
 
-  return base::make_optional(config_value);
+  return std::make_optional(config_value);
 }
 
-base::Optional<PerfCounter> ToPerfCounter(
+std::optional<PerfCounter> ToPerfCounter(
     std::string name,
     protos::gen::PerfEvents::Counter pb_enum) {
   using protos::gen::PerfEvents;
@@ -218,7 +218,7 @@
     default:
       PERFETTO_ELOG("Unrecognised PerfEvents::Counter enum value: %zu",
                     static_cast<size_t>(pb_enum));
-      return base::nullopt;
+      return std::nullopt;
   }
 }
 
@@ -294,10 +294,10 @@
 }
 
 // static
-base::Optional<EventConfig> EventConfig::Create(
+std::optional<EventConfig> EventConfig::Create(
     const protos::gen::PerfEventConfig& pb_config,
     const DataSourceConfig& raw_ds_config,
-    base::Optional<ProcessSharding> process_sharding,
+    std::optional<ProcessSharding> process_sharding,
     tracepoint_id_fn_t tracepoint_id_lookup) {
   // Timebase: sampling interval.
   uint64_t sampling_frequency = 0;
@@ -321,15 +321,15 @@
     auto maybe_counter =
         ToPerfCounter(timebase_name, pb_config.timebase().counter());
     if (!maybe_counter)
-      return base::nullopt;
+      return std::nullopt;
     timebase_event = *maybe_counter;
 
   } else if (pb_config.timebase().has_tracepoint()) {
     const auto& tracepoint_pb = pb_config.timebase().tracepoint();
-    base::Optional<uint32_t> maybe_id =
+    std::optional<uint32_t> maybe_id =
         ParseTracepointAndResolveId(tracepoint_pb, tracepoint_id_lookup);
     if (!maybe_id)
-      return base::nullopt;
+      return std::nullopt;
     timebase_event = PerfCounter::Tracepoint(
         timebase_name, tracepoint_pb.name(), tracepoint_pb.filter(), *maybe_id);
 
@@ -370,7 +370,7 @@
         // enum value from the future that we don't yet know, refuse the config
         // TODO(rsavitski): double-check that both pbzero and ::gen propagate
         // unknown enum values.
-        return base::nullopt;
+        return std::nullopt;
     }
 
     // Process scoping. Sharding parameter is supplied from outside as it is
@@ -388,10 +388,10 @@
   }
 
   // Ring buffer options.
-  base::Optional<uint32_t> ring_buffer_pages =
+  std::optional<uint32_t> ring_buffer_pages =
       ChooseActualRingBufferPages(pb_config.ring_buffer_pages());
   if (!ring_buffer_pages.has_value())
-    return base::nullopt;
+    return std::nullopt;
 
   uint32_t read_tick_period_ms = pb_config.ring_buffer_read_period_ms()
                                      ? pb_config.ring_buffer_read_period_ms()
@@ -421,7 +421,7 @@
   PERFETTO_DLOG("Capping samples (not records) per tick to [%" PRIu64 "]",
                 samples_per_tick_limit);
   if (samples_per_tick_limit == 0)
-    return base::nullopt;
+    return std::nullopt;
 
   // Optional footprint controls.
   uint64_t max_enqueued_footprint_bytes =
diff --git a/src/profiling/perf/event_config.h b/src/profiling/perf/event_config.h
index 515a102..dd1a7e3 100644
--- a/src/profiling/perf/event_config.h
+++ b/src/profiling/perf/event_config.h
@@ -25,9 +25,9 @@
 #include <linux/perf_event.h>
 #include <stdint.h>
 #include <sys/types.h>
+#include <optional>
 
 #include "perfetto/base/flat_set.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/tracing/core/data_source_config.h"
 
 #include "protos/perfetto/common/perf_events.gen.h"
@@ -56,7 +56,7 @@
   std::vector<std::string> exclude_cmdlines;
   base::FlatSet<pid_t> pids;
   base::FlatSet<pid_t> exclude_pids;
-  base::Optional<ProcessSharding> process_sharding;
+  std::optional<ProcessSharding> process_sharding;
   uint32_t additional_cmdline_count = 0;
 };
 
@@ -118,10 +118,10 @@
   using tracepoint_id_fn_t =
       std::function<uint32_t(const std::string&, const std::string&)>;
 
-  static base::Optional<EventConfig> Create(
+  static std::optional<EventConfig> Create(
       const protos::gen::PerfEventConfig& pb_config,
       const DataSourceConfig& raw_ds_config,
-      base::Optional<ProcessSharding> process_sharding,
+      std::optional<ProcessSharding> process_sharding,
       tracepoint_id_fn_t tracepoint_id_lookup);
 
   uint32_t ring_buffer_pages() const { return ring_buffer_pages_; }
diff --git a/src/profiling/perf/event_config_unittest.cc b/src/profiling/perf/event_config_unittest.cc
index 95c0a9b..35ec628 100644
--- a/src/profiling/perf/event_config_unittest.cc
+++ b/src/profiling/perf/event_config_unittest.cc
@@ -19,9 +19,9 @@
 #include <linux/perf_event.h>
 #include <stdint.h>
 #include <time.h>
+#include <optional>
 
 #include "perfetto/base/logging.h"
-#include "perfetto/ext/base/optional.h"
 #include "test/gtest_and_gmock.h"
 
 #include "protos/perfetto/common/perf_events.gen.h"
@@ -38,20 +38,20 @@
   return (v != 0 && ((v & (v - 1)) == 0));
 }
 
-base::Optional<EventConfig> CreateEventConfig(
+std::optional<EventConfig> CreateEventConfig(
     const protos::gen::PerfEventConfig& perf_cfg,
     EventConfig::tracepoint_id_fn_t tracepoint_id_lookup =
         [](const std::string&, const std::string&) { return 0; }) {
   protos::gen::DataSourceConfig ds_cfg;
   ds_cfg.set_perf_event_config_raw(perf_cfg.SerializeAsString());
   return EventConfig::Create(perf_cfg, ds_cfg,
-                             /*process_sharding=*/base::nullopt,
+                             /*process_sharding=*/std::nullopt,
                              tracepoint_id_lookup);
 }
 
 TEST(EventConfigTest, AttrStructConstructed) {
   protos::gen::PerfEventConfig cfg;
-  base::Optional<EventConfig> event_config = CreateEventConfig(cfg);
+  std::optional<EventConfig> event_config = CreateEventConfig(cfg);
 
   ASSERT_TRUE(event_config.has_value());
   ASSERT_TRUE(event_config->perf_attr() != nullptr);
@@ -60,7 +60,7 @@
 TEST(EventConfigTest, RingBufferPagesValidated) {
   {  // if unset, a default is used
     protos::gen::PerfEventConfig cfg;
-    base::Optional<EventConfig> event_config = CreateEventConfig(cfg);
+    std::optional<EventConfig> event_config = CreateEventConfig(cfg);
 
     ASSERT_TRUE(event_config.has_value());
     ASSERT_GT(event_config->ring_buffer_pages(), 0u);
@@ -70,7 +70,7 @@
     uint32_t num_pages = 128;
     protos::gen::PerfEventConfig cfg;
     cfg.set_ring_buffer_pages(num_pages);
-    base::Optional<EventConfig> event_config = CreateEventConfig(cfg);
+    std::optional<EventConfig> event_config = CreateEventConfig(cfg);
 
     ASSERT_TRUE(event_config.has_value());
     ASSERT_EQ(event_config->ring_buffer_pages(), num_pages);
@@ -78,7 +78,7 @@
   {  // entire config rejected if not a power of two of pages
     protos::gen::PerfEventConfig cfg;
     cfg.set_ring_buffer_pages(7);
-    base::Optional<EventConfig> event_config = CreateEventConfig(cfg);
+    std::optional<EventConfig> event_config = CreateEventConfig(cfg);
 
     ASSERT_FALSE(event_config.has_value());
   }
@@ -87,7 +87,7 @@
 TEST(EventConfigTest, ReadTickPeriodDefaultedIfUnset) {
   {  // if unset, a default is used
     protos::gen::PerfEventConfig cfg;
-    base::Optional<EventConfig> event_config = CreateEventConfig(cfg);
+    std::optional<EventConfig> event_config = CreateEventConfig(cfg);
 
     ASSERT_TRUE(event_config.has_value());
     ASSERT_GT(event_config->read_tick_period_ms(), 0u);
@@ -96,7 +96,7 @@
     uint32_t period_ms = 250;
     protos::gen::PerfEventConfig cfg;
     cfg.set_ring_buffer_read_period_ms(period_ms);
-    base::Optional<EventConfig> event_config = CreateEventConfig(cfg);
+    std::optional<EventConfig> event_config = CreateEventConfig(cfg);
 
     ASSERT_TRUE(event_config.has_value());
     ASSERT_EQ(event_config->read_tick_period_ms(), period_ms);
@@ -106,7 +106,7 @@
 TEST(EventConfigTest, RemotePeriodTimeoutDefaultedIfUnset) {
   {  // if unset, a default is used
     protos::gen::PerfEventConfig cfg;
-    base::Optional<EventConfig> event_config = CreateEventConfig(cfg);
+    std::optional<EventConfig> event_config = CreateEventConfig(cfg);
 
     ASSERT_TRUE(event_config.has_value());
     ASSERT_GT(event_config->remote_descriptor_timeout_ms(), 0u);
@@ -115,7 +115,7 @@
     uint32_t timeout_ms = 300;
     protos::gen::PerfEventConfig cfg;
     cfg.set_remote_descriptor_timeout_ms(timeout_ms);
-    base::Optional<EventConfig> event_config = CreateEventConfig(cfg);
+    std::optional<EventConfig> event_config = CreateEventConfig(cfg);
 
     ASSERT_TRUE(event_config.has_value());
     ASSERT_EQ(event_config->remote_descriptor_timeout_ms(), timeout_ms);
@@ -126,7 +126,7 @@
   {  // period:
     protos::gen::PerfEventConfig cfg;
     cfg.mutable_timebase()->set_period(100);
-    base::Optional<EventConfig> event_config = CreateEventConfig(cfg);
+    std::optional<EventConfig> event_config = CreateEventConfig(cfg);
 
     ASSERT_TRUE(event_config.has_value());
     EXPECT_FALSE(event_config->perf_attr()->freq);
@@ -135,7 +135,7 @@
   {  // frequency:
     protos::gen::PerfEventConfig cfg;
     cfg.mutable_timebase()->set_frequency(4000);
-    base::Optional<EventConfig> event_config = CreateEventConfig(cfg);
+    std::optional<EventConfig> event_config = CreateEventConfig(cfg);
 
     ASSERT_TRUE(event_config.has_value());
     EXPECT_TRUE(event_config->perf_attr()->freq);
@@ -144,7 +144,7 @@
   {  // legacy frequency field:
     protos::gen::PerfEventConfig cfg;
     cfg.set_sampling_frequency(5000);
-    base::Optional<EventConfig> event_config = CreateEventConfig(cfg);
+    std::optional<EventConfig> event_config = CreateEventConfig(cfg);
 
     ASSERT_TRUE(event_config.has_value());
     EXPECT_TRUE(event_config->perf_attr()->freq);
@@ -152,7 +152,7 @@
   }
   {  // default is 10 Hz (implementation-defined)
     protos::gen::PerfEventConfig cfg;
-    base::Optional<EventConfig> event_config = CreateEventConfig(cfg);
+    std::optional<EventConfig> event_config = CreateEventConfig(cfg);
 
     ASSERT_TRUE(event_config.has_value());
     EXPECT_TRUE(event_config->perf_attr()->freq);
@@ -171,8 +171,7 @@
         cfg.mutable_timebase()->mutable_tracepoint();
     mutable_tracepoint->set_name("sched:sched_switch");
 
-    base::Optional<EventConfig> event_config =
-        CreateEventConfig(cfg, id_lookup);
+    std::optional<EventConfig> event_config = CreateEventConfig(cfg, id_lookup);
 
     ASSERT_TRUE(event_config.has_value());
     EXPECT_EQ(event_config->perf_attr()->type, PERF_TYPE_TRACEPOINT);
@@ -181,7 +180,7 @@
   {  // default is the CPU timer:
     protos::gen::PerfEventConfig cfg;
     cfg.mutable_timebase()->set_frequency(1000);
-    base::Optional<EventConfig> event_config = CreateEventConfig(cfg);
+    std::optional<EventConfig> event_config = CreateEventConfig(cfg);
 
     ASSERT_TRUE(event_config.has_value());
     EXPECT_EQ(event_config->perf_attr()->type, PERF_TYPE_SOFTWARE);
@@ -199,7 +198,7 @@
     mutable_scope->set_additional_cmdline_count(3);
     mutable_scope->add_exclude_cmdline("heapprofd");
 
-    base::Optional<EventConfig> event_config = CreateEventConfig(cfg);
+    std::optional<EventConfig> event_config = CreateEventConfig(cfg);
 
     ASSERT_TRUE(event_config.has_value());
     const auto& filter = event_config->filter();
@@ -220,7 +219,7 @@
     cfg.set_additional_cmdline_count(3);
     cfg.add_exclude_cmdline("heapprofd");
 
-    base::Optional<EventConfig> event_config = CreateEventConfig(cfg);
+    std::optional<EventConfig> event_config = CreateEventConfig(cfg);
 
     ASSERT_TRUE(event_config.has_value());
     const auto& filter = event_config->filter();
@@ -241,7 +240,7 @@
     mutable_timebase->set_period(500);
     mutable_timebase->set_counter(protos::gen::PerfEvents::HW_CPU_CYCLES);
 
-    base::Optional<EventConfig> event_config = CreateEventConfig(cfg);
+    std::optional<EventConfig> event_config = CreateEventConfig(cfg);
 
     ASSERT_TRUE(event_config.has_value());
     EXPECT_EQ(event_config->perf_attr()->type, PERF_TYPE_HARDWARE);
@@ -256,7 +255,7 @@
     mutable_timebase->set_period(500);
     mutable_timebase->set_counter(protos::gen::PerfEvents::SW_PAGE_FAULTS);
 
-    base::Optional<EventConfig> event_config = CreateEventConfig(cfg);
+    std::optional<EventConfig> event_config = CreateEventConfig(cfg);
 
     ASSERT_TRUE(event_config.has_value());
     EXPECT_EQ(event_config->perf_attr()->type, PERF_TYPE_SOFTWARE);
@@ -272,7 +271,7 @@
     protos::gen::PerfEventConfig cfg;
     cfg.mutable_callstack_sampling();  // set field
 
-    base::Optional<EventConfig> event_config = CreateEventConfig(cfg);
+    std::optional<EventConfig> event_config = CreateEventConfig(cfg);
 
     ASSERT_TRUE(event_config.has_value());
     EXPECT_TRUE(event_config->sample_callstacks());
@@ -292,7 +291,7 @@
     cfg.mutable_callstack_sampling()->set_user_frames(
         protos::gen::PerfEventConfig::UNWIND_SKIP);
 
-    base::Optional<EventConfig> event_config = CreateEventConfig(cfg);
+    std::optional<EventConfig> event_config = CreateEventConfig(cfg);
 
     ASSERT_TRUE(event_config.has_value());
     EXPECT_TRUE(event_config->sample_callstacks());
@@ -315,7 +314,7 @@
   {
     protos::gen::PerfEventConfig cfg;
     cfg.mutable_callstack_sampling()->set_kernel_frames(true);
-    base::Optional<EventConfig> event_config = CreateEventConfig(cfg);
+    std::optional<EventConfig> event_config = CreateEventConfig(cfg);
 
     ASSERT_TRUE(event_config.has_value());
     EXPECT_TRUE(event_config->kernel_frames());
@@ -324,14 +323,14 @@
     protos::gen::PerfEventConfig cfg;
     cfg.set_all_cpus(true);  // used to detect compat mode
     cfg.set_kernel_frames(true);
-    base::Optional<EventConfig> event_config = CreateEventConfig(cfg);
+    std::optional<EventConfig> event_config = CreateEventConfig(cfg);
 
     ASSERT_TRUE(event_config.has_value());
     EXPECT_TRUE(event_config->kernel_frames());
   }
   {  // default is false
     protos::gen::PerfEventConfig cfg;
-    base::Optional<EventConfig> event_config = CreateEventConfig(cfg);
+    std::optional<EventConfig> event_config = CreateEventConfig(cfg);
 
     ASSERT_TRUE(event_config.has_value());
     EXPECT_FALSE(event_config->kernel_frames());
@@ -341,7 +340,7 @@
 TEST(EventConfigTest, TimestampClockId) {
   {  // if unset, a default is used
     protos::gen::PerfEventConfig cfg;
-    base::Optional<EventConfig> event_config = CreateEventConfig(cfg);
+    std::optional<EventConfig> event_config = CreateEventConfig(cfg);
 
     ASSERT_TRUE(event_config.has_value());
     EXPECT_TRUE(event_config->perf_attr()->use_clockid);
@@ -351,7 +350,7 @@
     protos::gen::PerfEventConfig cfg;
     cfg.mutable_timebase()->set_timestamp_clock(
         protos::gen::PerfEvents::PERF_CLOCK_BOOTTIME);
-    base::Optional<EventConfig> event_config = CreateEventConfig(cfg);
+    std::optional<EventConfig> event_config = CreateEventConfig(cfg);
 
     ASSERT_TRUE(event_config.has_value());
     EXPECT_TRUE(event_config->perf_attr()->use_clockid);
@@ -361,7 +360,7 @@
     protos::gen::PerfEventConfig cfg;
     cfg.mutable_timebase()->set_timestamp_clock(
         protos::gen::PerfEvents::PERF_CLOCK_MONOTONIC);
-    base::Optional<EventConfig> event_config = CreateEventConfig(cfg);
+    std::optional<EventConfig> event_config = CreateEventConfig(cfg);
 
     ASSERT_TRUE(event_config.has_value());
     EXPECT_TRUE(event_config->perf_attr()->use_clockid);
diff --git a/src/profiling/perf/event_reader.cc b/src/profiling/perf/event_reader.cc
index ae6e187..2a40c6c 100644
--- a/src/profiling/perf/event_reader.cc
+++ b/src/profiling/perf/event_reader.cc
@@ -111,9 +111,8 @@
     PERFETTO_PLOG("failed munmap");
 }
 
-base::Optional<PerfRingBuffer> PerfRingBuffer::Allocate(
-    int perf_fd,
-    size_t data_page_count) {
+std::optional<PerfRingBuffer> PerfRingBuffer::Allocate(int perf_fd,
+                                                       size_t data_page_count) {
   // perf_event_open requires the ring buffer to be a power of two in size.
   PERFETTO_DCHECK(IsPowerOfTwo(data_page_count));
 
@@ -128,7 +127,7 @@
                          MAP_SHARED, perf_fd, 0);
   if (mmap_addr == MAP_FAILED) {
     PERFETTO_PLOG("failed mmap");
-    return base::nullopt;
+    return std::nullopt;
   }
 
   // Expected layout is [ metadata page ] [ data pages ... ]
@@ -139,7 +138,7 @@
 
   PERFETTO_DCHECK(IsPowerOfTwo(ret.data_buf_sz_));
 
-  return base::make_optional(std::move(ret));
+  return std::make_optional(std::move(ret));
 }
 
 // See |perf_output_put_handle| for the necessary synchronization between the
@@ -224,39 +223,39 @@
   return *this;
 }
 
-base::Optional<EventReader> EventReader::ConfigureEvents(
+std::optional<EventReader> EventReader::ConfigureEvents(
     uint32_t cpu,
     const EventConfig& event_cfg) {
   auto leader_fd = PerfEventOpen(cpu, event_cfg.perf_attr());
   if (!leader_fd) {
     PERFETTO_PLOG("Failed perf_event_open");
-    return base::nullopt;
+    return std::nullopt;
   }
   if (!MaybeApplyTracepointFilter(leader_fd.get(), event_cfg.timebase_event()))
-    return base::nullopt;
+    return std::nullopt;
 
   auto ring_buffer =
       PerfRingBuffer::Allocate(leader_fd.get(), event_cfg.ring_buffer_pages());
   if (!ring_buffer.has_value()) {
-    return base::nullopt;
+    return std::nullopt;
   }
   return EventReader(cpu, *event_cfg.perf_attr(), std::move(leader_fd),
                      std::move(ring_buffer.value()));
 }
 
-base::Optional<ParsedSample> EventReader::ReadUntilSample(
+std::optional<ParsedSample> EventReader::ReadUntilSample(
     std::function<void(uint64_t)> records_lost_callback) {
   for (;;) {
     char* event = ring_buffer_.ReadRecordNonconsuming();
     if (!event)
-      return base::nullopt;  // caught up with the writer
+      return std::nullopt;  // caught up with the writer
 
     auto* event_hdr = reinterpret_cast<const perf_event_header*>(event);
 
     if (event_hdr->type == PERF_RECORD_SAMPLE) {
       ParsedSample sample = ParseSampleRecord(cpu_, event);
       ring_buffer_.Consume(event_hdr->size);
-      return base::make_optional(std::move(sample));
+      return std::make_optional(std::move(sample));
     }
 
     if (event_hdr->type == PERF_RECORD_LOST) {
diff --git a/src/profiling/perf/event_reader.h b/src/profiling/perf/event_reader.h
index 92e7521..11a63bb 100644
--- a/src/profiling/perf/event_reader.h
+++ b/src/profiling/perf/event_reader.h
@@ -21,8 +21,8 @@
 #include <stdint.h>
 #include <sys/mman.h>
 #include <sys/types.h>
+#include <optional>
 
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/tracing/core/basic_types.h"
 #include "src/profiling/perf/common_types.h"
@@ -33,8 +33,8 @@
 
 class PerfRingBuffer {
  public:
-  static base::Optional<PerfRingBuffer> Allocate(int perf_fd,
-                                                 size_t data_page_count);
+  static std::optional<PerfRingBuffer> Allocate(int perf_fd,
+                                                size_t data_page_count);
 
   ~PerfRingBuffer();
 
@@ -71,14 +71,14 @@
 
 class EventReader {
  public:
-  static base::Optional<EventReader> ConfigureEvents(
+  static std::optional<EventReader> ConfigureEvents(
       uint32_t cpu,
       const EventConfig& event_cfg);
 
   // Consumes records from the ring buffer until either encountering a sample,
   // or catching up to the writer. The other record of interest
   // (PERF_RECORD_LOST) is handled via the given callback.
-  base::Optional<ParsedSample> ReadUntilSample(
+  std::optional<ParsedSample> ReadUntilSample(
       std::function<void(uint64_t)> lost_events_callback);
 
   void EnableEvents();
diff --git a/src/profiling/perf/perf_producer.cc b/src/profiling/perf/perf_producer.cc
index ee15555..0fb7be3 100644
--- a/src/profiling/perf/perf_producer.cc
+++ b/src/profiling/perf/perf_producer.cc
@@ -379,7 +379,7 @@
   // Unlikely: handle a callstack sampling option that shares a random decision
   // between all data sources within a tracing session. Instead of introducing
   // session-scoped data, we replicate the decision in each per-DS EventConfig.
-  base::Optional<ProcessSharding> process_sharding;
+  std::optional<ProcessSharding> process_sharding;
   uint32_t shard_count =
       event_config_pb.callstack_sampling().scope().process_shard_count();
   if (shard_count > 0) {
@@ -387,7 +387,7 @@
         GetOrChooseCallstackProcessShard(tracing_session_id, shard_count);
   }
 
-  base::Optional<EventConfig> event_config = EventConfig::Create(
+  std::optional<EventConfig> event_config = EventConfig::Create(
       event_config_pb, config, process_sharding, tracepoint_id_lookup);
   if (!event_config.has_value()) {
     PERFETTO_ELOG("PerfEventConfig rejected.");
@@ -397,7 +397,7 @@
   size_t num_cpus = NumberOfCpus();
   std::vector<EventReader> per_cpu_readers;
   for (uint32_t cpu = 0; cpu < num_cpus; cpu++) {
-    base::Optional<EventReader> event_reader =
+    std::optional<EventReader> event_reader =
         EventReader::ConfigureEvents(cpu, event_config.value());
     if (!event_reader.has_value()) {
       PERFETTO_ELOG("Failed to set up perf events for cpu%" PRIu32
@@ -632,7 +632,7 @@
   };
 
   for (uint64_t i = 0; i < max_samples; i++) {
-    base::Optional<ParsedSample> sample =
+    std::optional<ParsedSample> sample =
         reader->ReadUntilSample(records_lost_callback);
     if (!sample) {
       return false;  // caught up to the writer
@@ -1082,7 +1082,7 @@
 // * reuse a choice made previously by a data source within this tracing
 //   session. The config option requires that all data sources within one config
 //   have the same shard count.
-base::Optional<ProcessSharding> PerfProducer::GetOrChooseCallstackProcessShard(
+std::optional<ProcessSharding> PerfProducer::GetOrChooseCallstackProcessShard(
     uint64_t tracing_session_id,
     uint32_t shard_count) {
   for (auto& it : data_sources_) {
diff --git a/src/profiling/perf/perf_producer.h b/src/profiling/perf/perf_producer.h
index 31d5ff5..83174d3 100644
--- a/src/profiling/perf/perf_producer.h
+++ b/src/profiling/perf/perf_producer.h
@@ -17,16 +17,15 @@
 #ifndef SRC_PROFILING_PERF_PERF_PRODUCER_H_
 #define SRC_PROFILING_PERF_PERF_PRODUCER_H_
 
+#include <unistd.h>
 #include <map>
 #include <memory>
-
-#include <unistd.h>
+#include <optional>
 
 #include <unwindstack/Error.h>
 #include <unwindstack/Regs.h>
 
 #include "perfetto/base/task_runner.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/unix_socket.h"
 #include "perfetto/ext/base/weak_ptr.h"
@@ -240,7 +239,7 @@
 
   // Chooses a random parameter for a callstack sampling option. Done at this
   // level as the choice is shared by all data sources within a tracing session.
-  base::Optional<ProcessSharding> GetOrChooseCallstackProcessShard(
+  std::optional<ProcessSharding> GetOrChooseCallstackProcessShard(
       uint64_t tracing_session_id,
       uint32_t shard_count);
 
diff --git a/src/profiling/perf/perf_producer_unittest.cc b/src/profiling/perf/perf_producer_unittest.cc
index 22f4002..76b615a 100644
--- a/src/profiling/perf/perf_producer_unittest.cc
+++ b/src/profiling/perf/perf_producer_unittest.cc
@@ -17,9 +17,9 @@
 #include "src/profiling/perf/perf_producer.h"
 
 #include <stdint.h>
+#include <optional>
 
 #include "perfetto/base/logging.h"
-#include "perfetto/ext/base/optional.h"
 #include "test/gtest_and_gmock.h"
 
 namespace perfetto {
diff --git a/src/profiling/perf/unwinding.h b/src/profiling/perf/unwinding.h
index 08d6e86..8295fb8 100644
--- a/src/profiling/perf/unwinding.h
+++ b/src/profiling/perf/unwinding.h
@@ -17,18 +17,17 @@
 #ifndef SRC_PROFILING_PERF_UNWINDING_H_
 #define SRC_PROFILING_PERF_UNWINDING_H_
 
+#include <stdint.h>
 #include <condition_variable>
 #include <map>
+#include <optional>
 #include <thread>
 
 #include <linux/perf_event.h>
-#include <stdint.h>
-
 #include <unwindstack/Error.h>
 
 #include "perfetto/base/flat_set.h"
 #include "perfetto/base/logging.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/thread_checker.h"
 #include "perfetto/ext/base/unix_task_runner.h"
 #include "perfetto/ext/tracing/core/basic_types.h"
@@ -137,7 +136,7 @@
 
     Status status = Status::kInitial;
     // Present iff status == kFdsResolved.
-    base::Optional<UnwindingMetadata> unwind_state;
+    std::optional<UnwindingMetadata> unwind_state;
     // Used to distinguish first-time unwinding attempts for a process, for
     // logging purposes.
     bool attempted_unwinding = false;
diff --git a/src/profiling/symbolizer/breakpad_parser.cc b/src/profiling/symbolizer/breakpad_parser.cc
index 5c3c5c0..5e996a7 100644
--- a/src/profiling/symbolizer/breakpad_parser.cc
+++ b/src/profiling/symbolizer/breakpad_parser.cc
@@ -32,18 +32,18 @@
   return i < sym.start_address;
 }
 
-base::Optional<std::string> GetFileContents(const std::string& file_path) {
+std::optional<std::string> GetFileContents(const std::string& file_path) {
   std::string file_contents;
   base::ScopedFile fd = base::OpenFile(file_path, O_RDONLY);
   // Read the contents of the file into |file_contents|.
   if (!fd) {
-    return base::nullopt;
+    return std::nullopt;
   }
   if (!base::ReadFileDescriptor(fd.get(), &file_contents)) {
-    return base::nullopt;
+    return std::nullopt;
   }
 
-  return base::make_optional(std::move(file_contents));
+  return std::make_optional(std::move(file_contents));
 }
 
 // Parses the given string and determines if it begins with the label
@@ -65,7 +65,7 @@
     : file_path_(file_path) {}
 
 bool BreakpadParser::ParseFile() {
-  base::Optional<std::string> file_contents = GetFileContents(file_path_);
+  std::optional<std::string> file_contents = GetFileContents(file_path_);
   if (!file_contents) {
     PERFETTO_ELOG("Could not get file contents of %s.", file_path_.c_str());
     return false;
@@ -111,7 +111,7 @@
   return true;
 }
 
-base::Optional<std::string> BreakpadParser::GetSymbol(uint64_t address) const {
+std::optional<std::string> BreakpadParser::GetSymbol(uint64_t address) const {
   // Returns an iterator pointing to the first element where the symbol's start
   // address is greater than |address|.
   auto it = std::upper_bound(symbols_.begin(), symbols_.end(), address,
@@ -119,7 +119,7 @@
   // If the first symbol's address is greater than |address| then |address| is
   // too low to appear in |symbols_|.
   if (it == symbols_.begin()) {
-    return base::nullopt;
+    return std::nullopt;
   }
   // upper_bound() returns the first symbol who's start address is greater than
   // |address|. Therefore to find the symbol with a range of addresses that
@@ -130,7 +130,7 @@
       address < it->start_address + it->function_size) {
     return it->symbol_name;
   }
-  return base::nullopt;
+  return std::nullopt;
 }
 
 base::Status BreakpadParser::ParseIfFuncRecord(base::StringView current_line) {
@@ -170,7 +170,7 @@
   }
 
   // Get the start address.
-  base::Optional<uint64_t> optional_address =
+  std::optional<uint64_t> optional_address =
       base::CStringToUInt64(words.cur_token(), 16);
   if (!optional_address) {
     return base::Status("Address should be hexadecimal.");
@@ -179,7 +179,7 @@
 
   // Get the function size.
   words.Next();
-  base::Optional<size_t> optional_func_size =
+  std::optional<size_t> optional_func_size =
       base::CStringToUInt32(words.cur_token(), 16);
   if (!optional_func_size) {
     return base::Status("Function size should be hexadecimal.");
diff --git a/src/profiling/symbolizer/breakpad_parser.h b/src/profiling/symbolizer/breakpad_parser.h
index 0377353..8926a94 100644
--- a/src/profiling/symbolizer/breakpad_parser.h
+++ b/src/profiling/symbolizer/breakpad_parser.h
@@ -17,11 +17,11 @@
 #ifndef SRC_PROFILING_SYMBOLIZER_BREAKPAD_PARSER_H_
 #define SRC_PROFILING_SYMBOLIZER_BREAKPAD_PARSER_H_
 
+#include <optional>
 #include <string>
 #include <vector>
 
 #include "perfetto/base/status.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_view.h"
 
 namespace perfetto {
@@ -64,7 +64,7 @@
   // Returns the function name corresponding to |address| as a string. The
   // search is log(N) on the number of functions in the binary. |address| is the
   // relative offset from the start of the binary.
-  base::Optional<std::string> GetSymbol(uint64_t address) const;
+  std::optional<std::string> GetSymbol(uint64_t address) const;
 
   const std::vector<Symbol>& symbols_for_testing() const { return symbols_; }
 
diff --git a/src/profiling/symbolizer/breakpad_symbolizer.cc b/src/profiling/symbolizer/breakpad_symbolizer.cc
index c7558c7..763d34f 100644
--- a/src/profiling/symbolizer/breakpad_symbolizer.cc
+++ b/src/profiling/symbolizer/breakpad_symbolizer.cc
@@ -16,9 +16,10 @@
 
 #include "src/profiling/symbolizer/breakpad_symbolizer.h"
 
+#include <optional>
+
 #include "perfetto/base/build_config.h"
 #include "perfetto/ext/base/file_utils.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/ext/base/string_writer.h"
 #include "src/profiling/symbolizer/breakpad_parser.h"
@@ -83,7 +84,7 @@
   // Add each address's function name to the |result| vector in the same order.
   for (uint64_t addr : address) {
     SymbolizedFrame frame;
-    base::Optional<std::string> opt_func_name = parser.GetSymbol(addr);
+    std::optional<std::string> opt_func_name = parser.GetSymbol(addr);
     if (opt_func_name) {
       frame.function_name = *opt_func_name;
       num_symbolized_frames++;
diff --git a/src/profiling/symbolizer/local_symbolizer.cc b/src/profiling/symbolizer/local_symbolizer.cc
index ce11a40..0382556 100644
--- a/src/profiling/symbolizer/local_symbolizer.cc
+++ b/src/profiling/symbolizer/local_symbolizer.cc
@@ -20,6 +20,7 @@
 
 #include <cinttypes>
 #include <memory>
+#include <optional>
 #include <sstream>
 #include <string>
 #include <vector>
@@ -28,7 +29,6 @@
 #include "perfetto/base/compiler.h"
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/file_utils.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "src/profiling/symbolizer/elf.h"
@@ -126,17 +126,17 @@
 }
 
 template <typename E>
-base::Optional<uint64_t> GetLoadBias(void* mem, size_t size) {
+std::optional<uint64_t> GetLoadBias(void* mem, size_t size) {
   const typename E::Ehdr* ehdr = static_cast<typename E::Ehdr*>(mem);
   if (!InRange(mem, size, ehdr, sizeof(typename E::Ehdr))) {
     PERFETTO_ELOG("Corrupted ELF.");
-    return base::nullopt;
+    return std::nullopt;
   }
   for (size_t i = 0; i < ehdr->e_phnum; ++i) {
     typename E::Phdr* phdr = GetPhdr<E>(mem, ehdr, i);
     if (!InRange(mem, size, phdr, sizeof(typename E::Phdr))) {
       PERFETTO_ELOG("Corrupted ELF.");
-      return base::nullopt;
+      return std::nullopt;
     }
     if (phdr->p_type == PT_LOAD && phdr->p_flags & PF_X) {
       return phdr->p_vaddr - phdr->p_offset;
@@ -146,17 +146,17 @@
 }
 
 template <typename E>
-base::Optional<std::string> GetBuildId(void* mem, size_t size) {
+std::optional<std::string> GetBuildId(void* mem, size_t size) {
   const typename E::Ehdr* ehdr = static_cast<typename E::Ehdr*>(mem);
   if (!InRange(mem, size, ehdr, sizeof(typename E::Ehdr))) {
     PERFETTO_ELOG("Corrupted ELF.");
-    return base::nullopt;
+    return std::nullopt;
   }
   for (size_t i = 0; i < ehdr->e_shnum; ++i) {
     typename E::Shdr* shdr = GetShdr<E>(mem, ehdr, i);
     if (!InRange(mem, size, shdr, sizeof(typename E::Shdr))) {
       PERFETTO_ELOG("Corrupted ELF.");
-      return base::nullopt;
+      return std::nullopt;
     }
 
     if (shdr->sh_type != SHT_NOTE)
@@ -169,13 +169,13 @@
 
       if (!InRange(mem, size, nhdr, sizeof(typename E::Nhdr))) {
         PERFETTO_ELOG("Corrupted ELF.");
-        return base::nullopt;
+        return std::nullopt;
       }
       if (nhdr->n_type == NT_GNU_BUILD_ID && nhdr->n_namesz == 4) {
         char* name = reinterpret_cast<char*>(nhdr) + sizeof(*nhdr);
         if (!InRange(mem, size, name, 4)) {
           PERFETTO_ELOG("Corrupted ELF.");
-          return base::nullopt;
+          return std::nullopt;
         }
         if (memcmp(name, "GNU", 3) == 0) {
           const char* value = reinterpret_cast<char*>(nhdr) + sizeof(*nhdr) +
@@ -183,7 +183,7 @@
 
           if (!InRange(mem, size, value, nhdr->n_descsz)) {
             PERFETTO_ELOG("Corrupted ELF.");
-            return base::nullopt;
+            return std::nullopt;
           }
           return std::string(value, nhdr->n_descsz);
         }
@@ -192,7 +192,7 @@
                 base::AlignUp<4>(nhdr->n_descsz);
     }
   }
-  return base::nullopt;
+  return std::nullopt;
 }
 
 std::string SplitBuildID(const std::string& hex_build_id) {
@@ -217,23 +217,23 @@
   uint64_t load_bias;
 };
 
-base::Optional<BuildIdAndLoadBias> GetBuildIdAndLoadBias(const char* fname,
-                                                         size_t size) {
+std::optional<BuildIdAndLoadBias> GetBuildIdAndLoadBias(const char* fname,
+                                                        size_t size) {
   static_assert(EI_CLASS > EI_MAG3, "mem[EI_MAG?] accesses are in range.");
   if (size <= EI_CLASS)
-    return base::nullopt;
+    return std::nullopt;
   ScopedReadMmap map(fname, size);
   if (!map.IsValid()) {
     PERFETTO_PLOG("mmap");
-    return base::nullopt;
+    return std::nullopt;
   }
   char* mem = static_cast<char*>(*map);
 
   if (!IsElf(mem, size))
-    return base::nullopt;
+    return std::nullopt;
 
-  base::Optional<std::string> build_id;
-  base::Optional<uint64_t> load_bias;
+  std::optional<std::string> build_id;
+  std::optional<uint64_t> load_bias;
   switch (mem[EI_CLASS]) {
     case ELFCLASS32:
       build_id = GetBuildId<Elf32>(mem, size);
@@ -244,12 +244,12 @@
       load_bias = GetLoadBias<Elf64>(mem, size);
       break;
     default:
-      return base::nullopt;
+      return std::nullopt;
   }
   if (build_id && load_bias) {
     return BuildIdAndLoadBias{*build_id, *load_bias};
   }
-  return base::nullopt;
+  return std::nullopt;
 }
 
 std::map<std::string, FoundBinary> BuildIdIndex(std::vector<std::string> dirs) {
@@ -274,7 +274,7 @@
         return;
       }
     }
-    base::Optional<BuildIdAndLoadBias> build_id_and_load_bias =
+    std::optional<BuildIdAndLoadBias> build_id_and_load_bias =
         GetBuildIdAndLoadBias(fname, size);
     if (build_id_and_load_bias) {
       result.emplace(build_id_and_load_bias->build_id,
@@ -298,7 +298,7 @@
   *file_name = line.substr(0, row_pos);
   auto line_no_str = line.substr(row_pos + 1, col_pos - row_pos - 1);
 
-  base::Optional<int32_t> opt_parsed_line_no = base::StringToInt32(line_no_str);
+  std::optional<int32_t> opt_parsed_line_no = base::StringToInt32(line_no_str);
   if (!opt_parsed_line_no || *opt_parsed_line_no < 0)
     return false;
   *line_no = static_cast<uint32_t>(*opt_parsed_line_no);
@@ -310,7 +310,7 @@
 LocalBinaryIndexer::LocalBinaryIndexer(std::vector<std::string> roots)
     : buildid_to_file_(BuildIdIndex(std::move(roots))) {}
 
-base::Optional<FoundBinary> LocalBinaryIndexer::FindBinary(
+std::optional<FoundBinary> LocalBinaryIndexer::FindBinary(
     const std::string& abspath,
     const std::string& build_id) {
   auto it = buildid_to_file_.find(build_id);
@@ -318,7 +318,7 @@
     return it->second;
   PERFETTO_ELOG("Could not find Build ID: %s (file %s).",
                 base::ToHex(build_id).c_str(), abspath.c_str());
-  return base::nullopt;
+  return std::nullopt;
 }
 
 LocalBinaryIndexer::~LocalBinaryIndexer() = default;
@@ -326,14 +326,14 @@
 LocalBinaryFinder::LocalBinaryFinder(std::vector<std::string> roots)
     : roots_(std::move(roots)) {}
 
-base::Optional<FoundBinary> LocalBinaryFinder::FindBinary(
+std::optional<FoundBinary> LocalBinaryFinder::FindBinary(
     const std::string& abspath,
     const std::string& build_id) {
-  auto p = cache_.emplace(abspath, base::nullopt);
+  auto p = cache_.emplace(abspath, std::nullopt);
   if (!p.second)
     return p.first->second;
 
-  base::Optional<FoundBinary>& cache_entry = p.first->second;
+  std::optional<FoundBinary>& cache_entry = p.first->second;
 
   for (const std::string& root_str : roots_) {
     cache_entry = FindBinaryInRoot(root_str, abspath, build_id);
@@ -345,30 +345,30 @@
   return cache_entry;
 }
 
-base::Optional<FoundBinary> LocalBinaryFinder::IsCorrectFile(
+std::optional<FoundBinary> LocalBinaryFinder::IsCorrectFile(
     const std::string& symbol_file,
     const std::string& build_id) {
   if (!base::FileExists(symbol_file)) {
-    return base::nullopt;
+    return std::nullopt;
   }
   // Openfile opens the file with an exclusive lock on windows.
   size_t size = GetFileSize(symbol_file);
 
   if (size == 0) {
-    return base::nullopt;
+    return std::nullopt;
   }
 
-  base::Optional<BuildIdAndLoadBias> build_id_and_load_bias =
+  std::optional<BuildIdAndLoadBias> build_id_and_load_bias =
       GetBuildIdAndLoadBias(symbol_file.c_str(), size);
   if (!build_id_and_load_bias)
-    return base::nullopt;
+    return std::nullopt;
   if (build_id_and_load_bias->build_id != build_id) {
-    return base::nullopt;
+    return std::nullopt;
   }
   return FoundBinary{symbol_file, build_id_and_load_bias->load_bias};
 }
 
-base::Optional<FoundBinary> LocalBinaryFinder::FindBinaryInRoot(
+std::optional<FoundBinary> LocalBinaryFinder::FindBinaryInRoot(
     const std::string& root_str,
     const std::string& abspath,
     const std::string& build_id) {
@@ -404,7 +404,7 @@
   // * $ROOT/foo.so
   // * $ROOT/.build-id/ab/cd1234.debug
 
-  base::Optional<FoundBinary> result;
+  std::optional<FoundBinary> result;
 
   std::string symbol_file = root_str + "/" + dirname + "/" + filename;
   result = IsCorrectFile(symbol_file, build_id);
@@ -446,7 +446,7 @@
     }
   }
 
-  return base::nullopt;
+  return std::nullopt;
 }
 
 LocalBinaryFinder::~LocalBinaryFinder() = default;
@@ -507,7 +507,7 @@
     const std::string& build_id,
     uint64_t load_bias,
     const std::vector<uint64_t>& addresses) {
-  base::Optional<FoundBinary> binary =
+  std::optional<FoundBinary> binary =
       finder_->FindBinary(mapping_name, build_id);
   if (!binary)
     return {};
diff --git a/src/profiling/symbolizer/local_symbolizer.h b/src/profiling/symbolizer/local_symbolizer.h
index bb8b0c2..f92c553 100644
--- a/src/profiling/symbolizer/local_symbolizer.h
+++ b/src/profiling/symbolizer/local_symbolizer.h
@@ -17,12 +17,13 @@
 #ifndef SRC_PROFILING_SYMBOLIZER_LOCAL_SYMBOLIZER_H_
 #define SRC_PROFILING_SYMBOLIZER_LOCAL_SYMBOLIZER_H_
 
+#include <functional>
 #include <map>
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "src/profiling/symbolizer/subprocess.h"
 #include "src/profiling/symbolizer/symbolizer.h"
@@ -44,7 +45,7 @@
 class BinaryFinder {
  public:
   virtual ~BinaryFinder();
-  virtual base::Optional<FoundBinary> FindBinary(
+  virtual std::optional<FoundBinary> FindBinary(
       const std::string& abspath,
       const std::string& build_id) = 0;
 };
@@ -53,8 +54,8 @@
  public:
   explicit LocalBinaryIndexer(std::vector<std::string> roots);
 
-  base::Optional<FoundBinary> FindBinary(const std::string& abspath,
-                                         const std::string& build_id) override;
+  std::optional<FoundBinary> FindBinary(const std::string& abspath,
+                                        const std::string& build_id) override;
   ~LocalBinaryIndexer() override;
 
  private:
@@ -65,22 +66,22 @@
  public:
   explicit LocalBinaryFinder(std::vector<std::string> roots);
 
-  base::Optional<FoundBinary> FindBinary(const std::string& abspath,
-                                         const std::string& build_id) override;
+  std::optional<FoundBinary> FindBinary(const std::string& abspath,
+                                        const std::string& build_id) override;
 
   ~LocalBinaryFinder() override;
 
  private:
-  base::Optional<FoundBinary> IsCorrectFile(const std::string& symbol_file,
-                                            const std::string& build_id);
+  std::optional<FoundBinary> IsCorrectFile(const std::string& symbol_file,
+                                           const std::string& build_id);
 
-  base::Optional<FoundBinary> FindBinaryInRoot(const std::string& root_str,
-                                               const std::string& abspath,
-                                               const std::string& build_id);
+  std::optional<FoundBinary> FindBinaryInRoot(const std::string& root_str,
+                                              const std::string& abspath,
+                                              const std::string& build_id);
 
  private:
   std::vector<std::string> roots_;
-  std::map<std::string, base::Optional<FoundBinary>> cache_;
+  std::map<std::string, std::optional<FoundBinary>> cache_;
 };
 
 class LLVMSymbolizerProcess {
diff --git a/src/profiling/symbolizer/local_symbolizer_unittest.cc b/src/profiling/symbolizer/local_symbolizer_unittest.cc
index 919e3ff..04d4855 100644
--- a/src/profiling/symbolizer/local_symbolizer_unittest.cc
+++ b/src/profiling/symbolizer/local_symbolizer_unittest.cc
@@ -163,7 +163,7 @@
 
   LocalBinaryIndexer indexer({tmp.path() + "/dir1", tmp.path() + "/dir2"});
 
-  base::Optional<FoundBinary> bin1 =
+  std::optional<FoundBinary> bin1 =
       indexer.FindBinary("", "AAAAAAAAAAAAAAAAAAAA");
   ASSERT_TRUE(bin1.has_value());
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
@@ -171,7 +171,7 @@
 #else
   EXPECT_EQ(bin1.value().file_name, tmp.path() + "/dir1/elf1");
 #endif
-  base::Optional<FoundBinary> bin2 =
+  std::optional<FoundBinary> bin2 =
       indexer.FindBinary("", "BBBBBBBBBBBBBBBBBBBB");
   ASSERT_TRUE(bin2.has_value());
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
@@ -208,17 +208,17 @@
 
   LocalBinaryIndexer indexer({tmp.AbsolutePath("sym")});
 
-  base::Optional<FoundBinary> bin1 =
+  std::optional<FoundBinary> bin1 =
       indexer.FindBinary("", "AAAAAAAAAAAAAAAAAAAA");
   ASSERT_TRUE(bin1.has_value());
   EXPECT_EQ(bin1.value().file_name, tmp.AbsolutePath("sym/elf1"));
 
-  base::Optional<FoundBinary> bin2 =
+  std::optional<FoundBinary> bin2 =
       indexer.FindBinary("", "BBBBBBBBBBBBBBBBBBBB");
   ASSERT_TRUE(bin2.has_value());
   EXPECT_EQ(bin2.value().file_name, tmp.AbsolutePath("sym/dir1/elf2"));
 
-  base::Optional<FoundBinary> bin3 =
+  std::optional<FoundBinary> bin3 =
       indexer.FindBinary("", "CCCCCCCCCCCCCCCCCCCC");
   ASSERT_TRUE(bin3.has_value());
   EXPECT_EQ(bin3.value().file_name, tmp.AbsolutePath("sym/dir1/elf3"));
@@ -241,7 +241,7 @@
 
   LocalBinaryIndexer indexer({tmp.AbsolutePath("main")});
 
-  base::Optional<FoundBinary> bin1 =
+  std::optional<FoundBinary> bin1 =
       indexer.FindBinary("", "AAAAAAAAAAAAAAAAAAAA");
   ASSERT_TRUE(bin1.has_value());
   EXPECT_EQ(bin1.value().file_name, tmp.AbsolutePath("main/elf1"));
@@ -259,7 +259,7 @@
 
   LocalBinaryFinder finder({tmp.path() + "/root"});
 
-  base::Optional<FoundBinary> bin1 =
+  std::optional<FoundBinary> bin1 =
       finder.FindBinary("/dir/elf1.so", "AAAAAAAAAAAAAAAAAAAA");
   ASSERT_TRUE(bin1.has_value());
   EXPECT_EQ(bin1.value().file_name, tmp.path() + "/root/dir/elf1.so");
@@ -273,7 +273,7 @@
 
   LocalBinaryFinder finder({tmp.path() + "/root"});
 
-  base::Optional<FoundBinary> bin1 =
+  std::optional<FoundBinary> bin1 =
       finder.FindBinary("/dir/base.apk!elf1.so", "AAAAAAAAAAAAAAAAAAAA");
   ASSERT_TRUE(bin1.has_value());
   EXPECT_EQ(bin1.value().file_name, tmp.path() + "/root/dir/elf1.so");
@@ -286,7 +286,7 @@
 
   LocalBinaryFinder finder({tmp.path() + "/root"});
 
-  base::Optional<FoundBinary> bin1 =
+  std::optional<FoundBinary> bin1 =
       finder.FindBinary("/ignored_dir/elf1.so", "AAAAAAAAAAAAAAAAAAAA");
   ASSERT_TRUE(bin1.has_value());
   EXPECT_EQ(bin1.value().file_name, tmp.path() + "/root/elf1.so");
@@ -299,7 +299,7 @@
 
   LocalBinaryFinder finder({tmp.path() + "/root"});
 
-  base::Optional<FoundBinary> bin1 = finder.FindBinary(
+  std::optional<FoundBinary> bin1 = finder.FindBinary(
       "/ignored_dir/base.apk!elf1.so", "AAAAAAAAAAAAAAAAAAAA");
   ASSERT_TRUE(bin1.has_value());
   EXPECT_EQ(bin1.value().file_name, tmp.path() + "/root/elf1.so");
@@ -315,7 +315,7 @@
 
   LocalBinaryFinder finder({tmp.path() + "/root"});
 
-  base::Optional<FoundBinary> bin1 =
+  std::optional<FoundBinary> bin1 =
       finder.FindBinary("/ignored_dir/ignored_name.so", "AAAAAAAAAAAAAAAAAAAA");
   ASSERT_TRUE(bin1.has_value());
   EXPECT_EQ(
diff --git a/src/tools/multithreaded_alloc.cc b/src/tools/multithreaded_alloc.cc
index 4c5e603..a4490d9 100644
--- a/src/tools/multithreaded_alloc.cc
+++ b/src/tools/multithreaded_alloc.cc
@@ -23,13 +23,13 @@
 #include <condition_variable>
 #include <iterator>
 #include <mutex>
+#include <optional>
 #include <thread>
 #include <vector>
 
 #include "perfetto/base/logging.h"
 #include "perfetto/base/time.h"
 #include "perfetto/ext/base/getopt.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/heap_profile.h"
 
@@ -87,21 +87,21 @@
     PERFETTO_FATAL("%s NUMBER_THREADS RUNTIME_MS PENDING_ALLOCS", argv[0]);
   }
 
-  perfetto::base::Optional<uint64_t> opt_no_threads =
+  std::optional<uint64_t> opt_no_threads =
       perfetto::base::CStringToUInt64(argv[1]);
   if (!opt_no_threads) {
     PERFETTO_FATAL("Invalid number of threads: %s", argv[1]);
   }
   uint64_t no_threads = *opt_no_threads;
 
-  perfetto::base::Optional<uint64_t> opt_runtime_ms =
+  std::optional<uint64_t> opt_runtime_ms =
       perfetto::base::CStringToUInt64(argv[2]);
   if (!opt_runtime_ms) {
     PERFETTO_FATAL("Invalid runtime: %s", argv[2]);
   }
   uint64_t runtime_ms = *opt_runtime_ms;
 
-  perfetto::base::Optional<uint64_t> opt_pending_allocs =
+  std::optional<uint64_t> opt_pending_allocs =
       perfetto::base::CStringToUInt64(argv[3]);
   if (!opt_runtime_ms) {
     PERFETTO_FATAL("Invalid number of pending allocs: %s", argv[3]);
diff --git a/src/tools/proto_merger/proto_file.cc b/src/tools/proto_merger/proto_file.cc
index 653d271..bf1a715 100644
--- a/src/tools/proto_merger/proto_file.cc
+++ b/src/tools/proto_merger/proto_file.cc
@@ -60,8 +60,8 @@
         "repeated",  // LABEL_REPEATED
 };
 
-base::Optional<std::string> MinimizeType(const std::string& a,
-                                         const std::string& b) {
+std::optional<std::string> MinimizeType(const std::string& a,
+                                        const std::string& b) {
   auto a_pieces = base::SplitString(a, ".");
   auto b_pieces = base::SplitString(b, ".");
 
@@ -71,7 +71,7 @@
       return a.substr(skip);
     skip += a_pieces[i].size() + 1;
   }
-  return base::nullopt;
+  return std::nullopt;
 }
 
 std::string SimpleFieldTypeFromDescriptor(
diff --git a/src/tools/proto_merger/proto_merger.cc b/src/tools/proto_merger/proto_merger.cc
index 6bd30ce..ba7a701 100644
--- a/src/tools/proto_merger/proto_merger.cc
+++ b/src/tools/proto_merger/proto_merger.cc
@@ -16,19 +16,20 @@
 
 #include "src/tools/proto_merger/proto_merger.h"
 
+#include <optional>
+
 #include "perfetto/base/logging.h"
 #include "perfetto/base/status.h"
-#include "perfetto/ext/base/optional.h"
 
 namespace perfetto {
 namespace proto_merger {
 namespace {
 
 template <typename Key, typename Value>
-base::Optional<Value> FindInMap(const std::map<Key, Value>& map,
-                                const Key& key) {
+std::optional<Value> FindInMap(const std::map<Key, Value>& map,
+                               const Key& key) {
   auto it = map.find(key);
-  return it == map.end() ? base::nullopt : base::make_optional(it->second);
+  return it == map.end() ? std::nullopt : std::make_optional(it->second);
 }
 
 // Finds the given 'name' in the vector by comparing against
diff --git a/src/trace_processor/containers/nullable_vector.h b/src/trace_processor/containers/nullable_vector.h
index 44bb084..0880b12 100644
--- a/src/trace_processor/containers/nullable_vector.h
+++ b/src/trace_processor/containers/nullable_vector.h
@@ -20,9 +20,9 @@
 #include <stdint.h>
 
 #include <deque>
+#include <optional>
 
 #include "perfetto/base/logging.h"
-#include "perfetto/ext/base/optional.h"
 #include "src/trace_processor/containers/row_map.h"
 
 namespace perfetto {
@@ -67,14 +67,14 @@
   // Creates a dense nullable vector
   static NullableVector<T> Dense() { return NullableVector<T>(Mode::kDense); }
 
-  // Returns the optional value at |idx| or base::nullopt if the value is null.
-  base::Optional<T> Get(uint32_t idx) const {
+  // Returns the optional value at |idx| or std::nullopt if the value is null.
+  std::optional<T> Get(uint32_t idx) const {
     bool contains = valid_.IsSet(idx);
     if (mode_ == Mode::kDense) {
-      return contains ? base::make_optional(data_[idx]) : base::nullopt;
+      return contains ? std::make_optional(data_[idx]) : std::nullopt;
     } else {
-      return contains ? base::make_optional(data_[valid_.CountSetBits(idx)])
-                      : base::nullopt;
+      return contains ? std::make_optional(data_[valid_.CountSetBits(idx)])
+                      : std::nullopt;
     }
   }
 
@@ -85,7 +85,7 @@
   }
 
   // Adds the given optional value to the NullableVector.
-  void Append(base::Optional<T> val) {
+  void Append(std::optional<T> val) {
     if (val) {
       Append(*val);
     } else {
diff --git a/src/trace_processor/containers/nullable_vector_unittest.cc b/src/trace_processor/containers/nullable_vector_unittest.cc
index 471f616..c256ec4 100644
--- a/src/trace_processor/containers/nullable_vector_unittest.cc
+++ b/src/trace_processor/containers/nullable_vector_unittest.cc
@@ -26,23 +26,23 @@
   NullableVector<int64_t> sv;
   sv.Append(10);
   sv.Append(20);
-  sv.Append(base::nullopt);
+  sv.Append(std::nullopt);
   sv.Append(40);
 
   ASSERT_FALSE(sv.IsDense());
   ASSERT_EQ(sv.size(), 4u);
-  ASSERT_EQ(sv.Get(0), base::Optional<int64_t>(10));
-  ASSERT_EQ(sv.Get(1), base::Optional<int64_t>(20));
-  ASSERT_EQ(sv.Get(2), base::nullopt);
-  ASSERT_EQ(sv.Get(3), base::Optional<int64_t>(40));
+  ASSERT_EQ(sv.Get(0), std::optional<int64_t>(10));
+  ASSERT_EQ(sv.Get(1), std::optional<int64_t>(20));
+  ASSERT_EQ(sv.Get(2), std::nullopt);
+  ASSERT_EQ(sv.Get(3), std::optional<int64_t>(40));
 }
 
 TEST(NullableVector, Set) {
   NullableVector<int64_t> sv;
   sv.Append(10);
   sv.Append(20);
-  sv.Append(base::nullopt);
-  sv.Append(base::nullopt);
+  sv.Append(std::nullopt);
+  sv.Append(std::nullopt);
   sv.Append(40);
 
   sv.Set(0, 15);
@@ -50,7 +50,7 @@
 
   ASSERT_EQ(*sv.Get(0), 15);
   ASSERT_EQ(*sv.Get(1), 20);
-  ASSERT_EQ(sv.Get(2), base::nullopt);
+  ASSERT_EQ(sv.Get(2), std::nullopt);
   ASSERT_EQ(*sv.Get(3), 30);
   ASSERT_EQ(*sv.Get(4), 40);
 }
@@ -64,27 +64,27 @@
 
   sv.Set(1, 22);
 
-  ASSERT_EQ(sv.Get(0), base::Optional<int64_t>(1));
-  ASSERT_EQ(sv.Get(1), base::Optional<int64_t>(22));
-  ASSERT_EQ(sv.Get(2), base::Optional<int64_t>(3));
-  ASSERT_EQ(sv.Get(3), base::Optional<int64_t>(4));
+  ASSERT_EQ(sv.Get(0), std::optional<int64_t>(1));
+  ASSERT_EQ(sv.Get(1), std::optional<int64_t>(22));
+  ASSERT_EQ(sv.Get(2), std::optional<int64_t>(3));
+  ASSERT_EQ(sv.Get(3), std::optional<int64_t>(4));
 }
 
 TEST(NullableVector, Dense) {
   auto sv = NullableVector<int64_t>::Dense();
 
   sv.Append(0);
-  sv.Append(base::nullopt);
+  sv.Append(std::nullopt);
   sv.Append(2);
   sv.Append(3);
-  sv.Append(base::nullopt);
+  sv.Append(std::nullopt);
 
   ASSERT_TRUE(sv.IsDense());
   ASSERT_EQ(sv.Get(0), 0);
-  ASSERT_EQ(sv.Get(1), base::nullopt);
+  ASSERT_EQ(sv.Get(1), std::nullopt);
   ASSERT_EQ(sv.Get(2), 2);
   ASSERT_EQ(sv.Get(3), 3);
-  ASSERT_EQ(sv.Get(4), base::nullopt);
+  ASSERT_EQ(sv.Get(4), std::nullopt);
 
   sv.Set(1, 1);
   ASSERT_EQ(sv.Get(1), 1);
diff --git a/src/trace_processor/containers/row_map.h b/src/trace_processor/containers/row_map.h
index 06d650a..8ab3915 100644
--- a/src/trace_processor/containers/row_map.h
+++ b/src/trace_processor/containers/row_map.h
@@ -20,10 +20,10 @@
 #include <stdint.h>
 
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include "perfetto/base/logging.h"
-#include "perfetto/ext/base/optional.h"
 #include "src/trace_processor/containers/bit_vector.h"
 #include "src/trace_processor/containers/bit_vector_iterators.h"
 
@@ -305,24 +305,24 @@
   }
 
   // Returns the first row of the given |index| in the RowMap.
-  base::Optional<InputRow> RowOf(OutputIndex index) const {
+  std::optional<InputRow> RowOf(OutputIndex index) const {
     switch (mode_) {
       case Mode::kRange: {
         if (index < start_index_ || index >= end_index_)
-          return base::nullopt;
+          return std::nullopt;
         return index - start_index_;
       }
       case Mode::kBitVector: {
         return index < bit_vector_.size() && bit_vector_.IsSet(index)
-                   ? base::make_optional(bit_vector_.CountSetBits(index))
-                   : base::nullopt;
+                   ? std::make_optional(bit_vector_.CountSetBits(index))
+                   : std::nullopt;
       }
       case Mode::kIndexVector: {
         auto it = std::find(index_vector_.begin(), index_vector_.end(), index);
         return it != index_vector_.end()
-                   ? base::make_optional(static_cast<InputRow>(
+                   ? std::make_optional(static_cast<InputRow>(
                          std::distance(index_vector_.begin(), it)))
-                   : base::nullopt;
+                   : std::nullopt;
       }
     }
     PERFETTO_FATAL("For GCC");
diff --git a/src/trace_processor/containers/row_map_unittest.cc b/src/trace_processor/containers/row_map_unittest.cc
index 5be6c06..6e1ca11 100644
--- a/src/trace_processor/containers/row_map_unittest.cc
+++ b/src/trace_processor/containers/row_map_unittest.cc
@@ -34,11 +34,11 @@
   ASSERT_EQ(rm.Get(1), 31u);
   ASSERT_EQ(rm.Get(16), 46u);
 
-  ASSERT_EQ(rm.RowOf(29), base::nullopt);
+  ASSERT_EQ(rm.RowOf(29), std::nullopt);
   ASSERT_EQ(rm.RowOf(30), 0u);
   ASSERT_EQ(rm.RowOf(37), 7u);
   ASSERT_EQ(rm.RowOf(46), 16u);
-  ASSERT_EQ(rm.RowOf(47), base::nullopt);
+  ASSERT_EQ(rm.RowOf(47), std::nullopt);
 }
 
 TEST(RowMapUnittest, SmokeBitVector) {
@@ -54,8 +54,8 @@
   ASSERT_EQ(rm.RowOf(4u), 1u);
   ASSERT_EQ(rm.RowOf(5u), 2u);
 
-  ASSERT_EQ(rm.RowOf(1u), base::nullopt);
-  ASSERT_EQ(rm.RowOf(100u), base::nullopt);
+  ASSERT_EQ(rm.RowOf(1u), std::nullopt);
+  ASSERT_EQ(rm.RowOf(100u), std::nullopt);
 }
 
 TEST(RowMapUnittest, SmokeIndexVector) {
diff --git a/src/trace_processor/containers/string_pool.h b/src/trace_processor/containers/string_pool.h
index ddc57cd..73e0e2b 100644
--- a/src/trace_processor/containers/string_pool.h
+++ b/src/trace_processor/containers/string_pool.h
@@ -21,11 +21,11 @@
 #include <stdint.h>
 
 #include <limits>
+#include <optional>
 #include <vector>
 
 #include "perfetto/ext/base/flat_hash_map.h"
 #include "perfetto/ext/base/hash.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/paged_memory.h"
 #include "perfetto/protozero/proto_utils.h"
 #include "src/trace_processor/containers/null_term_string_view.h"
@@ -132,7 +132,7 @@
     return *id;
   }
 
-  base::Optional<Id> GetId(base::StringView str) const {
+  std::optional<Id> GetId(base::StringView str) const {
     if (str.data() == nullptr)
       return Id::Null();
 
@@ -142,7 +142,7 @@
       PERFETTO_DCHECK(Get(*id) == str);
       return *id;
     }
-    return base::nullopt;
+    return std::nullopt;
   }
 
   NullTermStringView Get(Id id) const {
diff --git a/src/trace_processor/db/column.cc b/src/trace_processor/db/column.cc
index 4fec8fc..01c6b5e 100644
--- a/src/trace_processor/db/column.cc
+++ b/src/trace_processor/db/column.cc
@@ -56,16 +56,16 @@
     bool is_storage_dense;
     switch (type_) {
       case ColumnType::kInt32:
-        is_storage_dense = storage<base::Optional<int32_t>>().IsDense();
+        is_storage_dense = storage<std::optional<int32_t>>().IsDense();
         break;
       case ColumnType::kUint32:
-        is_storage_dense = storage<base::Optional<uint32_t>>().IsDense();
+        is_storage_dense = storage<std::optional<uint32_t>>().IsDense();
         break;
       case ColumnType::kInt64:
-        is_storage_dense = storage<base::Optional<int64_t>>().IsDense();
+        is_storage_dense = storage<std::optional<int64_t>>().IsDense();
         break;
       case ColumnType::kDouble:
-        is_storage_dense = storage<base::Optional<double>>().IsDense();
+        is_storage_dense = storage<std::optional<double>>().IsDense();
         break;
       case ColumnType::kString:
         PERFETTO_FATAL("String column should not be nullable");
@@ -159,7 +159,7 @@
     PERFETTO_DCHECK(value.is_null());
     if (is_nullable) {
       overlay().FilterInto(rm, [this](uint32_t row) {
-        return !storage<base::Optional<T>>().Get(row).has_value();
+        return !storage<std::optional<T>>().Get(row).has_value();
       });
     } else {
       rm->Clear();
@@ -169,7 +169,7 @@
     PERFETTO_DCHECK(value.is_null());
     if (is_nullable) {
       overlay().FilterInto(rm, [this](uint32_t row) {
-        return storage<base::Optional<T>>().Get(row).has_value();
+        return storage<std::optional<T>>().Get(row).has_value();
       });
     }
     return;
@@ -227,7 +227,7 @@
     case FilterOp::kLt:
       overlay().FilterInto(rm, [this, &cmp](uint32_t idx) {
         if (is_nullable) {
-          auto opt_value = storage<base::Optional<T>>().Get(idx);
+          auto opt_value = storage<std::optional<T>>().Get(idx);
           return opt_value && cmp(*opt_value) < 0;
         }
         return cmp(storage<T>().Get(idx)) < 0;
@@ -236,7 +236,7 @@
     case FilterOp::kEq:
       overlay().FilterInto(rm, [this, &cmp](uint32_t idx) {
         if (is_nullable) {
-          auto opt_value = storage<base::Optional<T>>().Get(idx);
+          auto opt_value = storage<std::optional<T>>().Get(idx);
           return opt_value && cmp(*opt_value) == 0;
         }
         return cmp(storage<T>().Get(idx)) == 0;
@@ -245,7 +245,7 @@
     case FilterOp::kGt:
       overlay().FilterInto(rm, [this, &cmp](uint32_t idx) {
         if (is_nullable) {
-          auto opt_value = storage<base::Optional<T>>().Get(idx);
+          auto opt_value = storage<std::optional<T>>().Get(idx);
           return opt_value && cmp(*opt_value) > 0;
         }
         return cmp(storage<T>().Get(idx)) > 0;
@@ -254,7 +254,7 @@
     case FilterOp::kNe:
       overlay().FilterInto(rm, [this, &cmp](uint32_t idx) {
         if (is_nullable) {
-          auto opt_value = storage<base::Optional<T>>().Get(idx);
+          auto opt_value = storage<std::optional<T>>().Get(idx);
           return opt_value && cmp(*opt_value) != 0;
         }
         return cmp(storage<T>().Get(idx)) != 0;
@@ -263,7 +263,7 @@
     case FilterOp::kLe:
       overlay().FilterInto(rm, [this, &cmp](uint32_t idx) {
         if (is_nullable) {
-          auto opt_value = storage<base::Optional<T>>().Get(idx);
+          auto opt_value = storage<std::optional<T>>().Get(idx);
           return opt_value && cmp(*opt_value) <= 0;
         }
         return cmp(storage<T>().Get(idx)) <= 0;
@@ -272,7 +272,7 @@
     case FilterOp::kGe:
       overlay().FilterInto(rm, [this, &cmp](uint32_t idx) {
         if (is_nullable) {
-          auto opt_value = storage<base::Optional<T>>().Get(idx);
+          auto opt_value = storage<std::optional<T>>().Get(idx);
           return opt_value && cmp(*opt_value) >= 0;
         }
         return cmp(storage<T>().Get(idx)) >= 0;
@@ -486,8 +486,8 @@
 
   overlay().StableSort(out, [this](uint32_t a_idx, uint32_t b_idx) {
     if (is_nullable) {
-      auto a_val = storage<base::Optional<T>>().Get(a_idx);
-      auto b_val = storage<base::Optional<T>>().Get(b_idx);
+      auto a_val = storage<std::optional<T>>().Get(a_idx);
+      auto b_val = storage<std::optional<T>>().Get(b_idx);
 
       int res = compare::NullableNumeric(a_val, b_val);
       return desc ? res > 0 : res < 0;
diff --git a/src/trace_processor/db/column.h b/src/trace_processor/db/column.h
index 1d1b347..04425b9 100644
--- a/src/trace_processor/db/column.h
+++ b/src/trace_processor/db/column.h
@@ -18,9 +18,9 @@
 #define SRC_TRACE_PROCESSOR_DB_COLUMN_H_
 
 #include <stdint.h>
+#include <optional>
 
 #include "perfetto/base/logging.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/trace_processor/basic_types.h"
 #include "src/trace_processor/containers/row_map.h"
 #include "src/trace_processor/containers/string_pool.h"
@@ -100,7 +100,7 @@
   static constexpr ColumnType ToColumnType() { return ColumnType::kString; }
 };
 template <typename T>
-struct ColumnTypeHelper<base::Optional<T>> : public ColumnTypeHelper<T> {};
+struct ColumnTypeHelper<std::optional<T>> : public ColumnTypeHelper<T> {};
 
 class Table;
 
@@ -262,7 +262,7 @@
   SqlValue Get(uint32_t row) const { return GetAtIdx(overlay().Get(row)); }
 
   // Returns the row containing the given value in the Column.
-  base::Optional<uint32_t> IndexOf(SqlValue value) const {
+  std::optional<uint32_t> IndexOf(SqlValue value) const {
     switch (type_) {
       // TODO(lalitm): investigate whether we could make this more efficient
       // by first checking the type of the column and comparing explicitly
@@ -276,11 +276,11 @@
           if (compare::SqlValue(Get(i), value) == 0)
             return i;
         }
-        return base::nullopt;
+        return std::nullopt;
       }
       case ColumnType::kId: {
         if (value.type != SqlValue::Type::kLong)
-          return base::nullopt;
+          return std::nullopt;
         return overlay().RowOf(static_cast<uint32_t>(value.long_value));
       }
       case ColumnType::kDummy:
@@ -328,11 +328,11 @@
     FilterIntoSlow(op, value, rm);
   }
 
-  // Returns the minimum value in this column. Returns nullopt if this column
-  // is empty.
-  base::Optional<SqlValue> Min() const {
+  // Returns the minimum value in this column. Returns std::nullopt if this
+  // column is empty.
+  std::optional<SqlValue> Min() const {
     if (overlay().empty())
-      return base::nullopt;
+      return std::nullopt;
 
     if (IsSorted())
       return Get(0);
@@ -342,11 +342,11 @@
     return *std::min_element(b, e, &compare::SqlValueComparator);
   }
 
-  // Returns the minimum value in this column. Returns nullopt if this column
-  // is empty.
-  base::Optional<SqlValue> Max() const {
+  // Returns the minimum value in this column. Returns std::nullopt if this
+  // column is empty.
+  std::optional<SqlValue> Max() const {
     if (overlay().empty())
-      return base::nullopt;
+      return std::nullopt;
 
     if (IsSorted())
       return Get(overlay().size() - 1);
@@ -525,7 +525,7 @@
   template <typename T>
   SqlValue GetAtIdxTyped(uint32_t idx) const {
     if (IsNullable()) {
-      auto opt_value = storage<base::Optional<T>>().Get(idx);
+      auto opt_value = storage<std::optional<T>>().Get(idx);
       return opt_value ? ToSqlValue(*opt_value) : SqlValue();
     }
     return ToSqlValue(storage<T>().Get(idx));
diff --git a/src/trace_processor/db/column_storage.h b/src/trace_processor/db/column_storage.h
index 70c8385..6cf2f30 100644
--- a/src/trace_processor/db/column_storage.h
+++ b/src/trace_processor/db/column_storage.h
@@ -66,7 +66,7 @@
 
 // Class used for implementing storage for nullable columns.
 template <typename T>
-class ColumnStorage<base::Optional<T>> : public ColumnStorageBase {
+class ColumnStorage<std::optional<T>> : public ColumnStorageBase {
  public:
   ColumnStorage() = default;
 
@@ -76,19 +76,19 @@
   ColumnStorage(ColumnStorage&&) = default;
   ColumnStorage& operator=(ColumnStorage&&) noexcept = default;
 
-  base::Optional<T> Get(uint32_t idx) const { return nv_.Get(idx); }
+  std::optional<T> Get(uint32_t idx) const { return nv_.Get(idx); }
   void Append(T val) { nv_.Append(val); }
-  void Append(base::Optional<T> val) { nv_.Append(std::move(val)); }
+  void Append(std::optional<T> val) { nv_.Append(std::move(val)); }
   void Set(uint32_t idx, T val) { nv_.Set(idx, val); }
   uint32_t size() const { return nv_.size(); }
   bool IsDense() const { return nv_.IsDense(); }
   void ShrinkToFit() { nv_.ShrinkToFit(); }
 
   template <bool IsDense>
-  static ColumnStorage<base::Optional<T>> Create() {
+  static ColumnStorage<std::optional<T>> Create() {
     return IsDense
-               ? ColumnStorage<base::Optional<T>>(NullableVector<T>::Dense())
-               : ColumnStorage<base::Optional<T>>(NullableVector<T>::Sparse());
+               ? ColumnStorage<std::optional<T>>(NullableVector<T>::Dense())
+               : ColumnStorage<std::optional<T>>(NullableVector<T>::Sparse());
   }
 
  private:
diff --git a/src/trace_processor/db/column_storage_overlay.h b/src/trace_processor/db/column_storage_overlay.h
index dad6c82..108dccd 100644
--- a/src/trace_processor/db/column_storage_overlay.h
+++ b/src/trace_processor/db/column_storage_overlay.h
@@ -20,10 +20,10 @@
 #include <stdint.h>
 
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include "perfetto/base/logging.h"
-#include "perfetto/ext/base/optional.h"
 #include "src/trace_processor/containers/bit_vector.h"
 #include "src/trace_processor/containers/bit_vector_iterators.h"
 #include "src/trace_processor/containers/row_map.h"
@@ -113,7 +113,7 @@
   OutputIndex Get(uint32_t row) const { return row_map_.Get(row); }
 
   // Returns the first row of the given |index| in the ColumnStorageOverlay.
-  base::Optional<InputRow> RowOf(OutputIndex index) const {
+  std::optional<InputRow> RowOf(OutputIndex index) const {
     return row_map_.RowOf(index);
   }
 
diff --git a/src/trace_processor/db/compare.h b/src/trace_processor/db/compare.h
index 3aeefbb..e118294 100644
--- a/src/trace_processor/db/compare.h
+++ b/src/trace_processor/db/compare.h
@@ -20,8 +20,8 @@
 #include <stdint.h>
 
 #include <algorithm>
+#include <optional>
 
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/trace_processor/basic_types.h"
 
@@ -112,7 +112,7 @@
 // This method was defined from observing the behaviour of SQLite when sorting
 // on columns containing nulls.
 template <typename T>
-inline int NullableNumeric(base::Optional<T> a, base::Optional<T> b) {
+inline int NullableNumeric(std::optional<T> a, std::optional<T> b) {
   if (!a)
     return b ? -1 : 0;
 
diff --git a/src/trace_processor/db/table.h b/src/trace_processor/db/table.h
index 21962bf..c1cf66c 100644
--- a/src/trace_processor/db/table.h
+++ b/src/trace_processor/db/table.h
@@ -21,10 +21,10 @@
 
 #include <limits>
 #include <numeric>
+#include <optional>
 #include <vector>
 
 #include "perfetto/base/logging.h"
-#include "perfetto/ext/base/optional.h"
 #include "src/trace_processor/containers/string_pool.h"
 #include "src/trace_processor/db/column.h"
 #include "src/trace_processor/db/column_storage_overlay.h"
@@ -148,19 +148,19 @@
   // Returns the column at index |idx| in the Table.
   const Column& GetColumn(uint32_t idx) const { return columns_[idx]; }
 
-  // Returns the column index with the given name or base::nullopt otherwise.
-  base::Optional<uint32_t> GetColumnIndexByName(const char* name) const {
+  // Returns the column index with the given name or std::nullopt otherwise.
+  std::optional<uint32_t> GetColumnIndexByName(const char* name) const {
     auto it = std::find_if(
         columns_.begin(), columns_.end(),
         [name](const Column& col) { return strcmp(col.name(), name) == 0; });
     if (it == columns_.end())
-      return base::nullopt;
+      return std::nullopt;
     return static_cast<uint32_t>(std::distance(columns_.begin(), it));
   }
 
   // Returns the column with the given name or nullptr otherwise.
   const Column* GetColumnByName(const char* name) const {
-    base::Optional<uint32_t> opt_idx = GetColumnIndexByName(name);
+    std::optional<uint32_t> opt_idx = GetColumnIndexByName(name);
     if (!opt_idx)
       return nullptr;
     return &columns_[*opt_idx];
diff --git a/src/trace_processor/db/table_unittest.cc b/src/trace_processor/db/table_unittest.cc
index e915ce2..6f370ca 100644
--- a/src/trace_processor/db/table_unittest.cc
+++ b/src/trace_processor/db/table_unittest.cc
@@ -15,7 +15,6 @@
  */
 
 #include "src/trace_processor/db/table.h"
-#include "perfetto/ext/base/optional.h"
 #include "src/trace_processor/db/typed_column.h"
 #include "src/trace_processor/tables/macros.h"
 
diff --git a/src/trace_processor/db/typed_column.h b/src/trace_processor/db/typed_column.h
index dae1b86..7bf0fad 100644
--- a/src/trace_processor/db/typed_column.h
+++ b/src/trace_processor/db/typed_column.h
@@ -91,7 +91,7 @@
   void Append(T v) { mutable_storage()->Append(Serializer::Serialize(v)); }
 
   // Returns the row containing the given value in the Column.
-  base::Optional<uint32_t> IndexOf(sql_value_type v) const {
+  std::optional<uint32_t> IndexOf(sql_value_type v) const {
     return Column::IndexOf(ToSqlValue(v));
   }
 
@@ -190,7 +190,7 @@
 
   Id operator[](uint32_t row) const { return Id(overlay().Get(row)); }
 
-  base::Optional<uint32_t> IndexOf(Id id) const {
+  std::optional<uint32_t> IndexOf(Id id) const {
     return overlay().RowOf(id.value);
   }
 
diff --git a/src/trace_processor/db/typed_column_internal.h b/src/trace_processor/db/typed_column_internal.h
index 5ffc811..634feac 100644
--- a/src/trace_processor/db/typed_column_internal.h
+++ b/src/trace_processor/db/typed_column_internal.h
@@ -34,10 +34,10 @@
   static serialized_type Serialize(T value) { return value; }
   static T Deserialize(serialized_type value) { return value; }
 
-  static base::Optional<serialized_type> Serialize(base::Optional<T> value) {
+  static std::optional<serialized_type> Serialize(std::optional<T> value) {
     return value;
   }
-  static base::Optional<T> Deserialize(base::Optional<serialized_type> value) {
+  static std::optional<T> Deserialize(std::optional<serialized_type> value) {
     return value;
   }
 };
@@ -53,11 +53,11 @@
   static serialized_type Serialize(T value) { return value.value; }
   static T Deserialize(serialized_type value) { return T{value}; }
 
-  static base::Optional<serialized_type> Serialize(base::Optional<T> value) {
-    return value ? base::make_optional(Serialize(*value)) : base::nullopt;
+  static std::optional<serialized_type> Serialize(std::optional<T> value) {
+    return value ? std::make_optional(Serialize(*value)) : std::nullopt;
   }
-  static base::Optional<T> Deserialize(base::Optional<serialized_type> value) {
-    return value ? base::make_optional(Deserialize(*value)) : base::nullopt;
+  static std::optional<T> Deserialize(std::optional<serialized_type> value) {
+    return value ? std::make_optional(Deserialize(*value)) : std::nullopt;
   }
 };
 
@@ -69,10 +69,10 @@
   static serialized_type Serialize(StringPool::Id value) { return value; }
   static StringPool::Id Deserialize(serialized_type value) { return value; }
 
-  static serialized_type Serialize(base::Optional<StringPool::Id> value) {
+  static serialized_type Serialize(std::optional<StringPool::Id> value) {
     // Since StringPool::Id == 0 is always treated as null, rewrite
-    // base::nullopt -> 0 to remove an extra check at filter time for
-    // base::nullopt. Instead, that code can assume that the ColumnStorage
+    // std::nullopt -> 0 to remove an extra check at filter time for
+    // std::nullopt. Instead, that code can assume that the ColumnStorage
     // layer always returns a valid id and can handle the nullability at the
     // stringpool level.
     // TODO(lalitm): remove this special casing if we migrate all tables over
@@ -80,8 +80,8 @@
     // in the stringpool.
     return value ? Serialize(*value) : StringPool::Id::Null();
   }
-  static base::Optional<serialized_type> Deserialize(
-      base::Optional<StringPool::Id> value) {
+  static std::optional<serialized_type> Deserialize(
+      std::optional<StringPool::Id> value) {
     return value;
   }
 };
@@ -113,12 +113,12 @@
 
 // Specialization for Optional types.
 template <typename T>
-struct TypeHandler<base::Optional<T>> {
+struct TypeHandler<std::optional<T>> {
   using non_optional_type = T;
   using sql_value_type =
       typename Serializer<non_optional_type>::serialized_type;
   using stored_type =
-      base::Optional<typename Serializer<non_optional_type>::serialized_type>;
+      std::optional<typename Serializer<non_optional_type>::serialized_type>;
 
   static constexpr bool is_optional = true;
   static constexpr bool is_string = false;
@@ -127,7 +127,7 @@
     return nv.Get(idx);
   }
 
-  static bool Equals(base::Optional<T> a, base::Optional<T> b) {
+  static bool Equals(std::optional<T> a, std::optional<T> b) {
     // We need to use equal_to here as it could be T == double and because we
     // enable all compile time warnings, we will get complaints if we just use
     // a == b. This is the same reason why we can't also just use equal_to using
@@ -138,7 +138,7 @@
   }
 };
 
-// Specialization for Optional<StringId> types.
+// Specialization for std::optional<StringId> types.
 template <>
 struct TypeHandler<StringPool::Id> {
   using non_optional_type = StringPool::Id;
@@ -156,30 +156,29 @@
   static bool Equals(StringPool::Id a, StringPool::Id b) { return a == b; }
 };
 
-// Specialization for Optional<StringId> types.
+// Specialization for std::optional<StringId> types.
 template <>
-struct TypeHandler<base::Optional<StringPool::Id>> {
-  // get_type removes the base::Optional since we convert base::nullopt ->
+struct TypeHandler<std::optional<StringPool::Id>> {
+  // get_type removes the base::Optional since we convert std::nullopt ->
   // StringPool::Id::Null (see Serializer<StringPool> above).
   using non_optional_type = StringPool::Id;
   using sql_value_type = NullTermStringView;
   using stored_type = StringPool::Id;
 
   // is_optional is false again because we always unwrap
-  // base::Optional<StringPool::Id> into StringPool::Id.
+  // std::optional<StringPool::Id> into StringPool::Id.
   static constexpr bool is_optional = false;
   static constexpr bool is_string = true;
 
-  static base::Optional<StringPool::Id> Get(
-      const ColumnStorage<stored_type>& nv,
-      uint32_t idx) {
+  static std::optional<StringPool::Id> Get(const ColumnStorage<stored_type>& nv,
+                                           uint32_t idx) {
     StringPool::Id id = nv.Get(idx);
-    return id.is_null() ? base::nullopt : base::make_optional(id);
+    return id.is_null() ? std::nullopt : std::make_optional(id);
   }
 
-  static bool Equals(base::Optional<StringPool::Id> a,
-                     base::Optional<StringPool::Id> b) {
-    // To match our handling of treating base::nullopt ==
+  static bool Equals(std::optional<StringPool::Id> a,
+                     std::optional<StringPool::Id> b) {
+    // To match our handling of treating std::nullopt ==
     // StringPool::Id::Null(), ensure that they both compare equal to each
     // other.
     return a == b || (!a && b->is_null()) || (!b && a->is_null());
diff --git a/src/trace_processor/db/view.cc b/src/trace_processor/db/view.cc
index 1855329..5b462c7 100644
--- a/src/trace_processor/db/view.cc
+++ b/src/trace_processor/db/view.cc
@@ -55,10 +55,10 @@
                           std::initializer_list<JoinTable> joins,
                           std::initializer_list<OutputColumn> cols,
                           View* view) {
-  // Insert the node for the root table; the column indices being nullopt
+  // Insert the node for the root table; the column indices being std::nullopt
   // indicates this is the root.
   std::unique_ptr<TableNode> root_node(
-      new TableNode{root_table, base::nullopt, base::nullopt, JoinFlag::kNoFlag,
+      new TableNode{root_table, std::nullopt, std::nullopt, JoinFlag::kNoFlag,
                     TableNode::Children{}});
   base::FlatHashMap<base::StringView, TableNode*> node_map;
   node_map.Insert(root_table_name, root_node.get());
@@ -78,7 +78,7 @@
     TableNode* prev_node = *prev_node_it;
 
     // Verify that the previous table's column exists.
-    base::Optional<uint32_t> opt_prev_col_idx =
+    std::optional<uint32_t> opt_prev_col_idx =
         prev_node->table->GetColumnIndexByName(join.prev_col);
     if (!opt_prev_col_idx) {
       return base::ErrStatus(
@@ -87,7 +87,7 @@
     }
 
     // Verify that the current table's column exists.
-    base::Optional<uint32_t> opt_col_idx =
+    std::optional<uint32_t> opt_col_idx =
         join.table->GetColumnIndexByName(join.col);
     if (!opt_col_idx) {
       return base::ErrStatus(
@@ -304,7 +304,7 @@
     right_rm_iv.reserve(left_rm.size());
     left_col.overlay().FilterInto(&left_rm, [&](uint32_t idx) {
       // Check if the right table has the value from the left table.
-      base::Optional<uint32_t> opt_idx =
+      std::optional<uint32_t> opt_idx =
           right_col.IndexOf(left_col.GetAtIdx(idx));
 
       // If it doesn't, return false indicating that this row should be
diff --git a/src/trace_processor/db/view.h b/src/trace_processor/db/view.h
index 66c8564..8f830f8 100644
--- a/src/trace_processor/db/view.h
+++ b/src/trace_processor/db/view.h
@@ -22,13 +22,13 @@
 #include <iterator>
 #include <memory>
 #include <numeric>
+#include <optional>
 #include <string>
 #include <vector>
 
 #include "perfetto/base/logging.h"
 #include "perfetto/base/status.h"
 #include "perfetto/ext/base/flat_hash_map.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/small_vector.h"
 #include "perfetto/trace_processor/iterator.h"
 #include "src/trace_processor/containers/bit_vector.h"
@@ -208,11 +208,11 @@
     // The index of the id column in |table|.
     // In practice, this will always be zero (as id columns are implicitly the
     // first column) but having this allows flexibility for the future.
-    base::Optional<uint32_t> join_col_idx;
+    std::optional<uint32_t> join_col_idx;
 
     // The index of the column in the parent table which is selecting the rows
     // in |table|.
-    base::Optional<uint32_t> parent_join_col_idx;
+    std::optional<uint32_t> parent_join_col_idx;
 
     // Set of bitwise-ORed flags modifying how the join should be perfomed. See
     // |JoinFlag| struct for potential flags.
diff --git a/src/trace_processor/export_json.cc b/src/trace_processor/export_json.cc
index 7709681..dcac72d 100644
--- a/src/trace_processor/export_json.cc
+++ b/src/trace_processor/export_json.cc
@@ -86,8 +86,8 @@
 const char kStrippedArgument[] = "__stripped__";
 
 const char* GetNonNullString(const TraceStorage* storage,
-                             base::Optional<StringId> id) {
-  return id == base::nullopt || *id == kNullStringId
+                             std::optional<StringId> id) {
+  return id == std::nullopt || *id == kNullStringId
              ? ""
              : storage->GetString(*id).c_str();
 }
@@ -608,7 +608,7 @@
                             args_sets_[set_id].toStyledString().c_str());
               return;
             }
-            base::Optional<uint32_t> index = base::StringToUInt32(s);
+            std::optional<uint32_t> index = base::StringToUInt32(s);
             if (PERFETTO_UNLIKELY(!index)) {
               PERFETTO_ELOG("Expected to be able to extract index from %s",
                             key_part.c_str());
@@ -683,7 +683,7 @@
     const auto& thread_table = storage_->thread_table();
     for (UniqueTid utid = 0; utid < thread_table.row_count(); utid++) {
       uint32_t exported_pid = 0;
-      base::Optional<UniquePid> upid = thread_table.upid()[utid];
+      std::optional<UniquePid> upid = thread_table.upid()[utid];
       if (upid) {
         auto exported_pid_it = upids_to_exported_pids_.find(*upid);
         PERFETTO_DCHECK(exported_pid_it != upids_to_exported_pids_.end());
@@ -742,7 +742,7 @@
 
     const auto& process_table = storage_->process_table();
     for (UniquePid upid = 0; upid < process_table.row_count(); ++upid) {
-      base::Optional<int64_t> start_timestamp_ns =
+      std::optional<int64_t> start_timestamp_ns =
           process_table.start_ts()[upid];
       if (!start_timestamp_ns.has_value())
         continue;
@@ -795,7 +795,7 @@
       event["pid"] = 0;
       event["tid"] = 0;
 
-      base::Optional<UniqueTid> legacy_utid;
+      std::optional<UniqueTid> legacy_utid;
       std::string legacy_phase;
 
       event["args"] = args_builder_.GetArgs(it.arg_set_id());  // Makes a copy.
@@ -837,10 +837,10 @@
       const auto& virtual_track_slices = storage_->virtual_track_slices();
 
       int64_t duration_ns = it.dur();
-      base::Optional<int64_t> thread_ts_ns;
-      base::Optional<int64_t> thread_duration_ns;
-      base::Optional<int64_t> thread_instruction_count;
-      base::Optional<int64_t> thread_instruction_delta;
+      std::optional<int64_t> thread_ts_ns;
+      std::optional<int64_t> thread_duration_ns;
+      std::optional<int64_t> thread_instruction_count;
+      std::optional<int64_t> thread_instruction_delta;
 
       if (it.thread_dur()) {
         thread_ts_ns = it.thread_ts();
@@ -849,7 +849,7 @@
         thread_instruction_delta = it.thread_instruction_delta();
       } else {
         SliceId id = it.id();
-        base::Optional<uint32_t> vtrack_slice_row =
+        std::optional<uint32_t> vtrack_slice_row =
             virtual_track_slices.FindRowForSliceId(id);
         if (vtrack_slice_row) {
           thread_ts_ns =
@@ -1054,25 +1054,25 @@
     return util::OkStatus();
   }
 
-  base::Optional<Json::Value> CreateFlowEventV1(uint32_t flow_id,
-                                                SliceId slice_id,
-                                                std::string name,
-                                                std::string cat,
-                                                Json::Value args,
-                                                bool flow_begin) {
+  std::optional<Json::Value> CreateFlowEventV1(uint32_t flow_id,
+                                               SliceId slice_id,
+                                               std::string name,
+                                               std::string cat,
+                                               Json::Value args,
+                                               bool flow_begin) {
     const auto& slices = storage_->slice_table();
     const auto& thread_tracks = storage_->thread_track_table();
 
     auto opt_slice_idx = slices.id().IndexOf(slice_id);
     if (!opt_slice_idx)
-      return base::nullopt;
+      return std::nullopt;
     uint32_t slice_idx = opt_slice_idx.value();
 
     TrackId track_id = storage_->slice_table().track_id()[slice_idx];
     auto opt_thread_track_idx = thread_tracks.id().IndexOf(track_id);
     // catapult only supports flow events attached to thread-track slices
     if (!opt_thread_track_idx)
-      return base::nullopt;
+      return std::nullopt;
 
     UniqueTid utid = thread_tracks.utid()[opt_thread_track_idx.value()];
     auto pid_and_tid = UtidToPidAndTid(utid);
@@ -1112,9 +1112,9 @@
       } else {
         auto opt_slice_out_idx = slice_table.id().IndexOf(slice_out);
         PERFETTO_DCHECK(opt_slice_out_idx.has_value());
-        base::Optional<StringId> cat_id =
+        std::optional<StringId> cat_id =
             slice_table.category()[opt_slice_out_idx.value()];
-        base::Optional<StringId> name_id =
+        std::optional<StringId> name_id =
             slice_table.name()[opt_slice_out_idx.value()];
         cat = GetNonNullString(storage_, cat_id);
         name = GetNonNullString(storage_, name_id);
@@ -1211,13 +1211,13 @@
   }
 
   util::Status ExportRawEvents() {
-    base::Optional<StringId> raw_legacy_event_key_id =
+    std::optional<StringId> raw_legacy_event_key_id =
         storage_->string_pool().GetId("track_event.legacy_event");
-    base::Optional<StringId> raw_legacy_system_trace_event_id =
+    std::optional<StringId> raw_legacy_system_trace_event_id =
         storage_->string_pool().GetId("chrome_event.legacy_system_trace");
-    base::Optional<StringId> raw_legacy_user_trace_event_id =
+    std::optional<StringId> raw_legacy_user_trace_event_id =
         storage_->string_pool().GetId("chrome_event.legacy_user_trace");
-    base::Optional<StringId> raw_chrome_metadata_event_id =
+    std::optional<StringId> raw_chrome_metadata_event_id =
         storage_->string_pool().GetId("chrome_event.metadata");
 
     const auto& events = storage_->raw_table();
@@ -1393,7 +1393,7 @@
       const auto& mappings = storage_->stack_profile_mapping_table();
 
       std::vector<std::string> callstack;
-      base::Optional<CallsiteId> opt_callsite_id = samples.callsite_id()[i];
+      std::optional<CallsiteId> opt_callsite_id = samples.callsite_id()[i];
 
       while (opt_callsite_id) {
         CallsiteId callsite_id = *opt_callsite_id;
@@ -1445,7 +1445,7 @@
       // For now, only do this when the trace has already been symbolized i.e.
       // are not directly output by Chrome, to avoid interfering with other
       // processing pipelines.
-      base::Optional<CallsiteId> opt_current_callsite_id =
+      std::optional<CallsiteId> opt_current_callsite_id =
           samples.callsite_id()[i];
 
       if (opt_current_callsite_id && storage_->symbol_table().row_count() > 0) {
@@ -1556,9 +1556,9 @@
 
   util::Status ExportMemorySnapshots() {
     const auto& memory_snapshots = storage_->memory_snapshot_table();
-    base::Optional<StringId> private_footprint_id =
+    std::optional<StringId> private_footprint_id =
         storage_->string_pool().GetId("chrome.private_footprint_kb");
-    base::Optional<StringId> peak_resident_set_id =
+    std::optional<StringId> peak_resident_set_id =
         storage_->string_pool().GetId("chrome.peak_resident_set_kb");
 
     for (uint32_t memory_index = 0; memory_index < memory_snapshots.row_count();
diff --git a/src/trace_processor/export_json_unittest.cc b/src/trace_processor/export_json_unittest.cc
index 823bc17..61d0976 100644
--- a/src/trace_processor/export_json_unittest.cc
+++ b/src/trace_processor/export_json_unittest.cc
@@ -830,16 +830,16 @@
 
 TEST_F(ExportJsonTest, DuplicatePidAndTid) {
   UniqueTid upid1 = context_.process_tracker->StartNewProcess(
-      base::nullopt, base::nullopt, 1, kNullStringId,
+      std::nullopt, std::nullopt, 1, kNullStringId,
       ThreadNamePriority::kTrackDescriptor);
   UniqueTid utid1a = context_.process_tracker->UpdateThread(1, 1);
   UniqueTid utid1b = context_.process_tracker->UpdateThread(2, 1);
-  UniqueTid utid1c = context_.process_tracker->StartNewThread(base::nullopt, 2);
+  UniqueTid utid1c = context_.process_tracker->StartNewThread(std::nullopt, 2);
   // Associate the new thread with its process.
   ASSERT_EQ(utid1c, context_.process_tracker->UpdateThread(2, 1));
 
   UniqueTid upid2 = context_.process_tracker->StartNewProcess(
-      base::nullopt, base::nullopt, 1, kNullStringId,
+      std::nullopt, std::nullopt, 1, kNullStringId,
       ThreadNamePriority::kTrackDescriptor);
   UniqueTid utid2a = context_.process_tracker->UpdateThread(1, 1);
   UniqueTid utid2b = context_.process_tracker->UpdateThread(2, 1);
@@ -1542,7 +1542,7 @@
        storage->InternString("bar_file"), 77});
   frames->mutable_symbol_set_id()->Set(frame_2.row, symbol_set_id);
 
-  auto frame_callsite_1 = callsites->Insert({0, base::nullopt, frame_1.id});
+  auto frame_callsite_1 = callsites->Insert({0, std::nullopt, frame_1.id});
 
   auto frame_callsite_2 =
       callsites->Insert({1, frame_callsite_1.id, frame_2.id});
diff --git a/src/trace_processor/importers/android_bugreport/android_bugreport_parser.cc b/src/trace_processor/importers/android_bugreport/android_bugreport_parser.cc
index 5e80c24..87aa0f5 100644
--- a/src/trace_processor/importers/android_bugreport/android_bugreport_parser.cc
+++ b/src/trace_processor/importers/android_bugreport/android_bugreport_parser.cc
@@ -17,9 +17,9 @@
 #include "src/trace_processor/importers/android_bugreport/android_bugreport_parser.h"
 
 #include <algorithm>
+#include <optional>
 
 #include "perfetto/base/logging.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/trace_processor/trace_blob.h"
 #include "perfetto/trace_processor/trace_blob_view.h"
@@ -247,7 +247,7 @@
   // Typical name: "bugreport-product-TP1A.220623.001-2022-06-24-16-24-37.txt".
   auto year_str = br_file->name().substr(
       br_file->name().size() - strlen("2022-12-31-23-59-00.txt"), 4);
-  base::Optional<int32_t> year = base::StringToInt32(year_str);
+  std::optional<int32_t> year = base::StringToInt32(year_str);
   if (!year.has_value()) {
     PERFETTO_ELOG("Could not parse the year from %s", br_file->name().c_str());
     return false;
diff --git a/src/trace_processor/importers/android_bugreport/android_log_parser.cc b/src/trace_processor/importers/android_bugreport/android_log_parser.cc
index cf42150..b825862 100644
--- a/src/trace_processor/importers/android_bugreport/android_log_parser.cc
+++ b/src/trace_processor/importers/android_bugreport/android_log_parser.cc
@@ -17,10 +17,10 @@
 #include "src/trace_processor/importers/android_bugreport/android_log_parser.h"
 
 #include <string.h>
+#include <optional>
 
 #include "perfetto/base/logging.h"
 #include "perfetto/base/time.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "src/trace_processor/types/trace_processor_context.h"
 
@@ -42,9 +42,9 @@
 //      input="123",  decimal_scale=1000 -> res=123
 //      input="1234", decimal_scale=1000 -> res=123
 //      input="1234", decimal_scale=1000000 -> res=123400
-base::Optional<int> ReadNumAndAdvance(base::StringView* it,
-                                      char sep,
-                                      int decimal_scale = 0) {
+std::optional<int> ReadNumAndAdvance(base::StringView* it,
+                                     char sep,
+                                     int decimal_scale = 0) {
   int num = 0;
   bool sep_found = false;
   size_t next_it = 0;
@@ -73,13 +73,14 @@
     invalid_chars_found = true;
   }
   if (!sep_found)
-    return base::nullopt;
+    return std::nullopt;
   // If we find non-digit characters, we want to still skip the token but return
-  // nullopt. The parser below relies on token skipping to deal with cases where
-  // the uid (which we don't care about) is literal ("root" rather than 0).
+  // std::nullopt. The parser below relies on token skipping to deal with cases
+  // where the uid (which we don't care about) is literal ("root" rather than
+  // 0).
   *it = it->substr(next_it);
   if (invalid_chars_found)
-    return base::nullopt;
+    return std::nullopt;
   return num;
 }
 
@@ -146,18 +147,18 @@
     // 06-24 16:24:23.441532 23153 23153 I wm_on_stop_called: message ...
     // 07-28 14:25:13.506  root     0     0 I x86/fpu : Supporting XSAVE feature
     // 0x002: 'SSE registers'
-    base::Optional<int> month = ReadNumAndAdvance(&it, '-');
-    base::Optional<int> day = ReadNumAndAdvance(&it, ' ');
-    base::Optional<int> hour = ReadNumAndAdvance(&it, ':');
-    base::Optional<int> minute = ReadNumAndAdvance(&it, ':');
-    base::Optional<int> sec = ReadNumAndAdvance(&it, '.');
-    base::Optional<int> ns = ReadNumAndAdvance(&it, ' ', 1000 * 1000 * 1000);
+    std::optional<int> month = ReadNumAndAdvance(&it, '-');
+    std::optional<int> day = ReadNumAndAdvance(&it, ' ');
+    std::optional<int> hour = ReadNumAndAdvance(&it, ':');
+    std::optional<int> minute = ReadNumAndAdvance(&it, ':');
+    std::optional<int> sec = ReadNumAndAdvance(&it, '.');
+    std::optional<int> ns = ReadNumAndAdvance(&it, ' ', 1000 * 1000 * 1000);
 
     if (fmt == LogcatFormat::kBugreport)
       ReadNumAndAdvance(&it, ' ');  // Skip the UID column.
 
-    base::Optional<int> pid = ReadNumAndAdvance(&it, ' ');
-    base::Optional<int> tid = ReadNumAndAdvance(&it, ' ');
+    std::optional<int> pid = ReadNumAndAdvance(&it, ' ');
+    std::optional<int> tid = ReadNumAndAdvance(&it, ' ');
 
     if (!month || !day || !hour || !minute || !sec || !ns || !pid || !tid) {
       ++parse_failures;
diff --git a/src/trace_processor/importers/common/args_tracker.cc b/src/trace_processor/importers/common/args_tracker.cc
index 113c590..69eca73 100644
--- a/src/trace_processor/importers/common/args_tracker.cc
+++ b/src/trace_processor/importers/common/args_tracker.cc
@@ -82,7 +82,7 @@
     ArgSetId set_id =
         context_->global_args_tracker->AddArgSet(&args_[0], i, next_rid_idx);
     if (col->IsNullable()) {
-      TypedColumn<base::Optional<uint32_t>>::FromColumn(col)->Set(row, set_id);
+      TypedColumn<std::optional<uint32_t>>::FromColumn(col)->Set(row, set_id);
     } else {
       TypedColumn<uint32_t>::FromColumn(col)->Set(row, set_id);
     }
diff --git a/src/trace_processor/importers/common/args_translation_table.cc b/src/trace_processor/importers/common/args_translation_table.cc
index d1559e0..74cdc34 100644
--- a/src/trace_processor/importers/common/args_translation_table.cc
+++ b/src/trace_processor/importers/common/args_translation_table.cc
@@ -14,10 +14,11 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/importers/common/args_translation_table.h"
-#include "perfetto/ext/base/optional.h"
+#include <optional>
+
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/string_view.h"
+#include "src/trace_processor/importers/common/args_translation_table.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -105,8 +106,8 @@
 void ArgsTranslationTable::TranslateArgs(
     const ArgsTracker::CompactArgSet& arg_set,
     ArgsTracker::BoundInserter& inserter) const {
-  base::Optional<uint64_t> mapping_id;
-  base::Optional<uint64_t> rel_pc;
+  std::optional<uint64_t> mapping_id;
+  std::optional<uint64_t> rel_pc;
 
   for (const auto& arg : arg_set) {
     const auto key_type =
@@ -119,7 +120,7 @@
     switch (*key_type) {
       case KeyType::kChromeHistogramHash: {
         inserter.AddArg(interned_chrome_histogram_hash_key_, arg.value);
-        const base::Optional<base::StringView> translated_value =
+        const std::optional<base::StringView> translated_value =
             TranslateChromeHistogramHash(arg.value.uint_value);
         if (translated_value) {
           inserter.AddArg(
@@ -130,7 +131,7 @@
       }
       case KeyType::kChromeUserEventHash: {
         inserter.AddArg(interned_chrome_user_event_hash_key_, arg.value);
-        const base::Optional<base::StringView> translated_value =
+        const std::optional<base::StringView> translated_value =
             TranslateChromeUserEventHash(arg.value.uint_value);
         if (translated_value) {
           inserter.AddArg(
@@ -142,7 +143,7 @@
       case KeyType::kChromePerformanceMarkMarkHash: {
         inserter.AddArg(interned_chrome_performance_mark_mark_hash_key_,
                         arg.value);
-        const base::Optional<base::StringView> translated_value =
+        const std::optional<base::StringView> translated_value =
             TranslateChromePerformanceMarkMarkHash(arg.value.uint_value);
         if (translated_value) {
           inserter.AddArg(
@@ -154,7 +155,7 @@
       case KeyType::kChromePerformanceMarkSiteHash: {
         inserter.AddArg(interned_chrome_performance_mark_site_hash_key_,
                         arg.value);
-        const base::Optional<base::StringView> translated_value =
+        const std::optional<base::StringView> translated_value =
             TranslateChromePerformanceMarkSiteHash(arg.value.uint_value);
         if (translated_value) {
           inserter.AddArg(
@@ -164,7 +165,7 @@
         break;
       }
       case KeyType::kClassName: {
-        const base::Optional<StringId> translated_class_name =
+        const std::optional<StringId> translated_class_name =
             TranslateClassName(arg.value.string_value);
         if (translated_class_name) {
           inserter.AddArg(arg.flat_key, arg.key,
@@ -187,7 +188,7 @@
   EmitMojoMethodLocation(mapping_id, rel_pc, inserter);
 }
 
-base::Optional<ArgsTranslationTable::KeyType>
+std::optional<ArgsTranslationTable::KeyType>
 ArgsTranslationTable::KeyIdAndTypeToEnum(StringId flat_key_id,
                                          StringId key_id,
                                          Variadic::Type type) const {
@@ -215,66 +216,66 @@
       return KeyType::kClassName;
     }
   }
-  return base::nullopt;
+  return std::nullopt;
 }
 
-base::Optional<base::StringView>
+std::optional<base::StringView>
 ArgsTranslationTable::TranslateChromeHistogramHash(uint64_t hash) const {
   auto* value = chrome_histogram_hash_to_name_.Find(hash);
   if (!value) {
-    return base::nullopt;
+    return std::nullopt;
   }
   return base::StringView(*value);
 }
 
-base::Optional<base::StringView>
+std::optional<base::StringView>
 ArgsTranslationTable::TranslateChromeUserEventHash(uint64_t hash) const {
   auto* value = chrome_user_event_hash_to_action_.Find(hash);
   if (!value) {
-    return base::nullopt;
+    return std::nullopt;
   }
   return base::StringView(*value);
 }
 
-base::Optional<base::StringView>
+std::optional<base::StringView>
 ArgsTranslationTable::TranslateChromePerformanceMarkSiteHash(
     uint64_t hash) const {
   auto* value = chrome_performance_mark_site_hash_to_name_.Find(hash);
   if (!value) {
-    return base::nullopt;
+    return std::nullopt;
   }
   return base::StringView(*value);
 }
 
-base::Optional<base::StringView>
+std::optional<base::StringView>
 ArgsTranslationTable::TranslateChromePerformanceMarkMarkHash(
     uint64_t hash) const {
   auto* value = chrome_performance_mark_mark_hash_to_name_.Find(hash);
   if (!value) {
-    return base::nullopt;
+    return std::nullopt;
   }
   return base::StringView(*value);
 }
 
-base::Optional<ArgsTranslationTable::SourceLocation>
+std::optional<ArgsTranslationTable::SourceLocation>
 ArgsTranslationTable::TranslateNativeSymbol(MappingId mapping_id,
                                             uint64_t rel_pc) const {
   auto loc =
       native_symbol_to_location_.Find(std::make_pair(mapping_id, rel_pc));
   if (!loc) {
-    return base::nullopt;
+    return std::nullopt;
   }
   return *loc;
 }
 
-base::Optional<StringId> ArgsTranslationTable::TranslateClassName(
+std::optional<StringId> ArgsTranslationTable::TranslateClassName(
     StringId obfuscated_class_name_id) const {
   return deobfuscation_mapping_table_.TranslateClass(obfuscated_class_name_id);
 }
 
 void ArgsTranslationTable::EmitMojoMethodLocation(
-    base::Optional<uint64_t> mapping_id,
-    base::Optional<uint64_t> rel_pc,
+    std::optional<uint64_t> mapping_id,
+    std::optional<uint64_t> rel_pc,
     ArgsTracker::BoundInserter& inserter) const {
   if (!mapping_id || !rel_pc) {
     return;
diff --git a/src/trace_processor/importers/common/args_translation_table.h b/src/trace_processor/importers/common/args_translation_table.h
index e1b3178..f5bb1b2 100644
--- a/src/trace_processor/importers/common/args_translation_table.h
+++ b/src/trace_processor/importers/common/args_translation_table.h
@@ -18,9 +18,9 @@
 #define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_ARGS_TRANSLATION_TABLE_H_
 
 #include <cstdint>
+#include <optional>
 
 #include "perfetto/ext/base/flat_hash_map.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_view.h"
 #include "src/trace_processor/importers/common/args_tracker.h"
 #include "src/trace_processor/importers/common/deobfuscation_mapping_table.h"
@@ -86,23 +86,23 @@
     deobfuscation_mapping_table_ = std::move(deobfuscation_mapping_table);
   }
 
-  base::Optional<base::StringView> TranslateChromeHistogramHashForTesting(
+  std::optional<base::StringView> TranslateChromeHistogramHashForTesting(
       uint64_t hash) const {
     return TranslateChromeHistogramHash(hash);
   }
-  base::Optional<base::StringView> TranslateChromeUserEventHashForTesting(
+  std::optional<base::StringView> TranslateChromeUserEventHashForTesting(
       uint64_t hash) const {
     return TranslateChromeUserEventHash(hash);
   }
-  base::Optional<base::StringView>
+  std::optional<base::StringView>
   TranslateChromePerformanceMarkSiteHashForTesting(uint64_t hash) const {
     return TranslateChromePerformanceMarkSiteHash(hash);
   }
-  base::Optional<base::StringView>
+  std::optional<base::StringView>
   TranslateChromePerformanceMarkMarkHashForTesting(uint64_t hash) const {
     return TranslateChromePerformanceMarkMarkHash(hash);
   }
-  base::Optional<StringId> TranslateClassNameForTesting(
+  std::optional<StringId> TranslateClassNameForTesting(
       StringId obfuscated_class_name_id) const {
     return TranslateClassName(obfuscated_class_name_id);
   }
@@ -180,29 +180,30 @@
   DeobfuscationMappingTable deobfuscation_mapping_table_;
 
   // Returns the corresponding SupportedKey enum if the table knows how to
-  // translate the argument with the given key and type, and nullopt otherwise.
-  base::Optional<KeyType> KeyIdAndTypeToEnum(StringId flat_key_id,
-                                             StringId key_id,
-                                             Variadic::Type type) const;
+  // translate the argument with the given key and type, and std::nullopt
+  // otherwise.
+  std::optional<KeyType> KeyIdAndTypeToEnum(StringId flat_key_id,
+                                            StringId key_id,
+                                            Variadic::Type type) const;
 
-  base::Optional<base::StringView> TranslateChromeHistogramHash(
+  std::optional<base::StringView> TranslateChromeHistogramHash(
       uint64_t hash) const;
-  base::Optional<base::StringView> TranslateChromeUserEventHash(
+  std::optional<base::StringView> TranslateChromeUserEventHash(
       uint64_t hash) const;
-  base::Optional<base::StringView> TranslateChromePerformanceMarkSiteHash(
+  std::optional<base::StringView> TranslateChromePerformanceMarkSiteHash(
       uint64_t hash) const;
-  base::Optional<base::StringView> TranslateChromePerformanceMarkMarkHash(
+  std::optional<base::StringView> TranslateChromePerformanceMarkMarkHash(
       uint64_t hash) const;
-  base::Optional<SourceLocation> TranslateNativeSymbol(MappingId mapping_id,
-                                                       uint64_t rel_pc) const;
+  std::optional<SourceLocation> TranslateNativeSymbol(MappingId mapping_id,
+                                                      uint64_t rel_pc) const;
 
-  // Returns the deobfuscated name of a Java class or base::nullopt if
+  // Returns the deobfuscated name of a Java class or std::nullopt if
   // translation is not found.
-  base::Optional<StringId> TranslateClassName(
+  std::optional<StringId> TranslateClassName(
       StringId obfuscated_class_name_id) const;
 
-  void EmitMojoMethodLocation(base::Optional<uint64_t> mapping_id,
-                              base::Optional<uint64_t> rel_pc,
+  void EmitMojoMethodLocation(std::optional<uint64_t> mapping_id,
+                              std::optional<uint64_t> rel_pc,
                               ArgsTracker::BoundInserter& inserter) const;
 };
 
diff --git a/src/trace_processor/importers/common/args_translation_table_unittest.cc b/src/trace_processor/importers/common/args_translation_table_unittest.cc
index 904e607..e44b034 100644
--- a/src/trace_processor/importers/common/args_translation_table_unittest.cc
+++ b/src/trace_processor/importers/common/args_translation_table_unittest.cc
@@ -15,7 +15,9 @@
  */
 
 #include "src/trace_processor/importers/common/args_translation_table.h"
-#include "perfetto/ext/base/optional.h"
+
+#include <optional>
+
 #include "src/trace_processor/importers/common/deobfuscation_mapping_table.h"
 #include "test/gtest_and_gmock.h"
 
@@ -26,8 +28,8 @@
 TEST(ArgsTranslationTable, EmptyTableByDefault) {
   TraceStorage storage;
   ArgsTranslationTable table(&storage);
-  EXPECT_EQ(table.TranslateChromeHistogramHashForTesting(1), base::nullopt);
-  EXPECT_EQ(table.TranslateChromeUserEventHashForTesting(1), base::nullopt);
+  EXPECT_EQ(table.TranslateChromeHistogramHashForTesting(1), std::nullopt);
+  EXPECT_EQ(table.TranslateChromeUserEventHashForTesting(1), std::nullopt);
 }
 
 TEST(ArgsTranslationTable, TranslatesHistogramHashes) {
@@ -36,10 +38,10 @@
   table.AddChromeHistogramTranslationRule(1, "hash1");
   table.AddChromeHistogramTranslationRule(10, "hash2");
   EXPECT_EQ(table.TranslateChromeHistogramHashForTesting(1),
-            base::Optional<base::StringView>("hash1"));
+            std::optional<base::StringView>("hash1"));
   EXPECT_EQ(table.TranslateChromeHistogramHashForTesting(10),
-            base::Optional<base::StringView>("hash2"));
-  EXPECT_EQ(table.TranslateChromeHistogramHashForTesting(2), base::nullopt);
+            std::optional<base::StringView>("hash2"));
+  EXPECT_EQ(table.TranslateChromeHistogramHashForTesting(2), std::nullopt);
 }
 
 TEST(ArgsTranslationTable, TranslatesUserEventHashes) {
@@ -48,10 +50,10 @@
   table.AddChromeUserEventTranslationRule(1, "action1");
   table.AddChromeUserEventTranslationRule(10, "action2");
   EXPECT_EQ(table.TranslateChromeUserEventHashForTesting(1),
-            base::Optional<base::StringView>("action1"));
+            std::optional<base::StringView>("action1"));
   EXPECT_EQ(table.TranslateChromeUserEventHashForTesting(10),
-            base::Optional<base::StringView>("action2"));
-  EXPECT_EQ(table.TranslateChromeUserEventHashForTesting(2), base::nullopt);
+            std::optional<base::StringView>("action2"));
+  EXPECT_EQ(table.TranslateChromeUserEventHashForTesting(2), std::nullopt);
 }
 
 TEST(ArgsTranslationTable, TranslatesPerformanceMarkSiteHashes) {
@@ -60,11 +62,11 @@
   table.AddChromePerformanceMarkSiteTranslationRule(1, "hash1");
   table.AddChromePerformanceMarkSiteTranslationRule(10, "hash2");
   EXPECT_EQ(table.TranslateChromePerformanceMarkSiteHashForTesting(1),
-            base::Optional<base::StringView>("hash1"));
+            std::optional<base::StringView>("hash1"));
   EXPECT_EQ(table.TranslateChromePerformanceMarkSiteHashForTesting(10),
-            base::Optional<base::StringView>("hash2"));
+            std::optional<base::StringView>("hash2"));
   EXPECT_EQ(table.TranslateChromePerformanceMarkSiteHashForTesting(2),
-            base::nullopt);
+            std::nullopt);
 }
 
 TEST(ArgsTranslationTable, TranslatesPerformanceMarkMarkHashes) {
@@ -73,11 +75,11 @@
   table.AddChromePerformanceMarkMarkTranslationRule(1, "hash1");
   table.AddChromePerformanceMarkMarkTranslationRule(10, "hash2");
   EXPECT_EQ(table.TranslateChromePerformanceMarkMarkHashForTesting(1),
-            base::Optional<base::StringView>("hash1"));
+            std::optional<base::StringView>("hash1"));
   EXPECT_EQ(table.TranslateChromePerformanceMarkMarkHashForTesting(10),
-            base::Optional<base::StringView>("hash2"));
+            std::optional<base::StringView>("hash2"));
   EXPECT_EQ(table.TranslateChromePerformanceMarkMarkHashForTesting(2),
-            base::nullopt);
+            std::nullopt);
 }
 
 TEST(ArgsTranslationTable, TranslateClassName) {
@@ -93,8 +95,8 @@
   table.AddDeobfuscationMappingTable(std::move(deobfuscation_mapping));
 
   EXPECT_EQ(table.TranslateClassNameForTesting(xyz_id),
-            base::Optional<StringId>(class_x_id));
-  EXPECT_EQ(table.TranslateClassNameForTesting(abc_id), base::nullopt);
+            std::optional<StringId>(class_x_id));
+  EXPECT_EQ(table.TranslateClassNameForTesting(abc_id), std::nullopt);
 }
 
 TEST(ArgsTranslationTable, NeedsTranslation) {
diff --git a/src/trace_processor/importers/common/clock_tracker.h b/src/trace_processor/importers/common/clock_tracker.h
index 81588e6..a4edd3c 100644
--- a/src/trace_processor/importers/common/clock_tracker.h
+++ b/src/trace_processor/importers/common/clock_tracker.h
@@ -22,12 +22,12 @@
 #include <array>
 #include <cinttypes>
 #include <map>
+#include <optional>
 #include <random>
 #include <set>
 #include <vector>
 
 #include "perfetto/base/logging.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/status_or.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "src/trace_processor/importers/common/metadata_tracker.h"
diff --git a/src/trace_processor/importers/common/clock_tracker_unittest.cc b/src/trace_processor/importers/common/clock_tracker_unittest.cc
index e625111..74a665f 100644
--- a/src/trace_processor/importers/common/clock_tracker_unittest.cc
+++ b/src/trace_processor/importers/common/clock_tracker_unittest.cc
@@ -16,9 +16,9 @@
 
 #include "src/trace_processor/importers/common/clock_tracker.h"
 
+#include <optional>
 #include <random>
 
-#include "perfetto/ext/base/optional.h"
 #include "src/trace_processor/importers/common/metadata_tracker.h"
 #include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/types/trace_processor_context.h"
diff --git a/src/trace_processor/importers/common/deobfuscation_mapping_table.cc b/src/trace_processor/importers/common/deobfuscation_mapping_table.cc
index af5bbf3..243295b 100644
--- a/src/trace_processor/importers/common/deobfuscation_mapping_table.cc
+++ b/src/trace_processor/importers/common/deobfuscation_mapping_table.cc
@@ -39,50 +39,50 @@
       .second;
 }
 
-base::Optional<StringId> DeobfuscationMappingTable::TranslateClass(
+std::optional<StringId> DeobfuscationMappingTable::TranslateClass(
     StringId obfuscated_class_name) const {
   if (PERFETTO_UNLIKELY(!default_package_id_.has_value())) {
-    return base::nullopt;
+    return std::nullopt;
   }
   return TranslateClass(default_package_id_.value(), obfuscated_class_name);
 }
 
-base::Optional<StringId> DeobfuscationMappingTable::TranslateClass(
+std::optional<StringId> DeobfuscationMappingTable::TranslateClass(
     const PackageId& package,
     StringId obfuscated_class_name) const {
   const ObfuscatedClassesToMembers* classes_translation_ptr =
       class_per_package_translation_.Find(package);
   if (classes_translation_ptr == nullptr) {
-    return base::nullopt;
+    return std::nullopt;
   }
   const ClassTranslation* class_translation_ptr =
       classes_translation_ptr->Find(obfuscated_class_name);
   if (class_translation_ptr == nullptr) {
-    return base::nullopt;
+    return std::nullopt;
   }
   return class_translation_ptr->deobfuscated_class_name;
 }
 
-base::Optional<StringId> DeobfuscationMappingTable::TranslateMember(
+std::optional<StringId> DeobfuscationMappingTable::TranslateMember(
     const PackageId& package,
     StringId obfuscated_class_name,
     StringId obfuscated_member) const {
   const ObfuscatedClassesToMembers* classes_translation_ptr =
       class_per_package_translation_.Find(package);
   if (classes_translation_ptr == nullptr) {
-    return base::nullopt;
+    return std::nullopt;
   }
 
   const ClassTranslation* class_translation_ptr =
       classes_translation_ptr->Find(obfuscated_class_name);
   if (class_translation_ptr == nullptr) {
-    return base::nullopt;
+    return std::nullopt;
   }
 
   const StringId* member_translation_ptr =
       class_translation_ptr->members.Find(obfuscated_member);
   if (member_translation_ptr == nullptr) {
-    return base::nullopt;
+    return std::nullopt;
   }
   return *member_translation_ptr;
 }
diff --git a/src/trace_processor/importers/common/deobfuscation_mapping_table.h b/src/trace_processor/importers/common/deobfuscation_mapping_table.h
index 7022b19..9beeab9 100644
--- a/src/trace_processor/importers/common/deobfuscation_mapping_table.h
+++ b/src/trace_processor/importers/common/deobfuscation_mapping_table.h
@@ -18,12 +18,12 @@
 #define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_DEOBFUSCATION_MAPPING_TABLE_H_
 
 #include <cstdint>
+#include <optional>
 #include <string>
 #include <vector>
 
 #include "perfetto/ext/base/flat_hash_map.h"
 #include "perfetto/ext/base/hash.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_view.h"
 #include "src/trace_processor/storage/trace_storage.h"
 
@@ -58,16 +58,16 @@
   // These functions return the deobfuscated class/member name from an
   // obfuscated class/member name.
   // If a package is not provided, the |default_package_id_| is used.
-  // If translation is not found, returns base::nullopt.
+  // If translation is not found, returns std::nullopt.
 
-  base::Optional<StringId> TranslateClass(StringId obfuscated_class_name) const;
+  std::optional<StringId> TranslateClass(StringId obfuscated_class_name) const;
 
-  base::Optional<StringId> TranslateClass(const PackageId& package,
-                                          StringId obfuscated_class_name) const;
+  std::optional<StringId> TranslateClass(const PackageId& package,
+                                         StringId obfuscated_class_name) const;
 
-  base::Optional<StringId> TranslateMember(const PackageId& package,
-                                           StringId obfuscated_class_name,
-                                           StringId obfuscated_member) const;
+  std::optional<StringId> TranslateMember(const PackageId& package,
+                                          StringId obfuscated_class_name,
+                                          StringId obfuscated_member) const;
 
  private:
   struct PackageIdHash {
@@ -94,7 +94,7 @@
   // We need this because curently TraceProcessor doesn't use the package
   // version of the arguments.
   // TODO(b/244700870): start use the package version of arguments.
-  base::Optional<PackageId> default_package_id_;
+  std::optional<PackageId> default_package_id_;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/common/deobfuscation_mapping_table_unittest.cc b/src/trace_processor/importers/common/deobfuscation_mapping_table_unittest.cc
index 5b24492..d663db4 100644
--- a/src/trace_processor/importers/common/deobfuscation_mapping_table_unittest.cc
+++ b/src/trace_processor/importers/common/deobfuscation_mapping_table_unittest.cc
@@ -30,8 +30,8 @@
   StringId xyz_id = storage.InternString("xyz");
 
   DeobfuscationMappingTable table;
-  EXPECT_EQ(table.TranslateClass(xyz_id), base::nullopt);
-  EXPECT_EQ(table.TranslateClass(PackageId{"app", 123}, xyz_id), base::nullopt);
+  EXPECT_EQ(table.TranslateClass(xyz_id), std::nullopt);
+  EXPECT_EQ(table.TranslateClass(PackageId{"app", 123}, xyz_id), std::nullopt);
 }
 
 TEST(DeobfuscationMappingTable, TranslateClassSingleInsert) {
@@ -45,8 +45,8 @@
                             base::FlatHashMap<StringId, StringId>{});
   EXPECT_EQ(table.TranslateClass(xyz_id), class_x_id);
   EXPECT_EQ(table.TranslateClass(PackageId{"app", 123}, xyz_id), class_x_id);
-  EXPECT_EQ(table.TranslateClass(PackageId{"app", 124}, xyz_id), base::nullopt);
-  EXPECT_EQ(table.TranslateClass(PackageId{"app", 123}, abc_id), base::nullopt);
+  EXPECT_EQ(table.TranslateClass(PackageId{"app", 124}, xyz_id), std::nullopt);
+  EXPECT_EQ(table.TranslateClass(PackageId{"app", 123}, abc_id), std::nullopt);
 }
 
 TEST(DeobfuscationMappingTable, TranslateClassMultipleInsert) {
@@ -65,11 +65,10 @@
   table.AddClassTranslation(PackageId{"app3", 123}, abc_id, class_a_id,
                             base::FlatHashMap<StringId, StringId>{});
   EXPECT_EQ(table.TranslateClass(xyz_id), class_x_id);
-  EXPECT_EQ(table.TranslateClass(abc_id), base::nullopt);
+  EXPECT_EQ(table.TranslateClass(abc_id), std::nullopt);
   EXPECT_EQ(table.TranslateClass(PackageId{"app1", 123}, xyz_id), class_x_id);
   EXPECT_EQ(table.TranslateClass(PackageId{"app2", 123}, xyz_id), class_y_id);
-  EXPECT_EQ(table.TranslateClass(PackageId{"app1", 123}, abc_id),
-            base::nullopt);
+  EXPECT_EQ(table.TranslateClass(PackageId{"app1", 123}, abc_id), std::nullopt);
 }
 
 TEST(DeobfuscationMappingTable, TranslateMember) {
@@ -95,11 +94,11 @@
   EXPECT_EQ(table.TranslateMember(PackageId{"app1", 123}, xyz_id, mmm_2_id),
             member_2_id);
   EXPECT_EQ(table.TranslateMember(PackageId{"app1", 123}, xyz_id, mmm_4_id),
-            base::nullopt);
+            std::nullopt);
   EXPECT_EQ(table.TranslateMember(PackageId{"app1", 123}, abc_id, mmm_2_id),
-            base::nullopt);
+            std::nullopt);
   EXPECT_EQ(table.TranslateMember(PackageId{"app1", 124}, xyz_id, mmm_2_id),
-            base::nullopt);
+            std::nullopt);
 }
 
 }  // namespace
diff --git a/src/trace_processor/importers/common/event_tracker.cc b/src/trace_processor/importers/common/event_tracker.cc
index cf59e40..0ed799b 100644
--- a/src/trace_processor/importers/common/event_tracker.cc
+++ b/src/trace_processor/importers/common/event_tracker.cc
@@ -17,9 +17,9 @@
 #include "src/trace_processor/importers/common/event_tracker.h"
 
 #include <math.h>
+#include <optional>
 
 #include "perfetto/base/logging.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/utils.h"
 #include "src/trace_processor/importers/common/args_tracker.h"
 #include "src/trace_processor/importers/common/process_tracker.h"
@@ -36,7 +36,7 @@
 
 EventTracker::~EventTracker() = default;
 
-base::Optional<CounterId> EventTracker::PushProcessCounterForThread(
+std::optional<CounterId> EventTracker::PushProcessCounterForThread(
     int64_t timestamp,
     double value,
     StringId name_id,
@@ -52,15 +52,15 @@
   return opt_id;
 }
 
-base::Optional<CounterId> EventTracker::PushCounter(int64_t timestamp,
-                                                    double value,
-                                                    TrackId track_id) {
+std::optional<CounterId> EventTracker::PushCounter(int64_t timestamp,
+                                                   double value,
+                                                   TrackId track_id) {
   if (timestamp < max_timestamp_) {
     PERFETTO_DLOG(
         "counter event (ts: %" PRId64 ") out of order by %.4f ms, skipping",
         timestamp, static_cast<double>(max_timestamp_ - timestamp) / 1e6);
     context_->storage->IncrementStats(stats::counter_events_out_of_order);
-    return base::nullopt;
+    return std::nullopt;
   }
   max_timestamp_ = timestamp;
 
@@ -68,7 +68,7 @@
   return counter_values->Insert({timestamp, track_id, value}).id;
 }
 
-base::Optional<CounterId> EventTracker::PushCounter(
+std::optional<CounterId> EventTracker::PushCounter(
     int64_t timestamp,
     double value,
     TrackId track_id,
@@ -85,7 +85,7 @@
   const auto& thread_table = context_->storage->thread_table();
   for (const auto& pending_counter : pending_upid_resolution_counter_) {
     UniqueTid utid = pending_counter.utid;
-    base::Optional<UniquePid> upid = thread_table.upid()[utid];
+    std::optional<UniquePid> upid = thread_table.upid()[utid];
 
     TrackId track_id = kInvalidTrackId;
     if (upid.has_value()) {
diff --git a/src/trace_processor/importers/common/event_tracker.h b/src/trace_processor/importers/common/event_tracker.h
index 16117f0..626e80e 100644
--- a/src/trace_processor/importers/common/event_tracker.h
+++ b/src/trace_processor/importers/common/event_tracker.h
@@ -42,16 +42,16 @@
 
   // Adds a counter event to the counters table returning the index of the
   // newly added row.
-  virtual base::Optional<CounterId> PushCounter(int64_t timestamp,
-                                                double value,
-                                                TrackId track_id);
+  virtual std::optional<CounterId> PushCounter(int64_t timestamp,
+                                               double value,
+                                               TrackId track_id);
 
   // Adds a counter event with args to the counters table returning the index of
   // the newly added row.
-  base::Optional<CounterId> PushCounter(int64_t timestamp,
-                                        double value,
-                                        TrackId track_id,
-                                        SetArgsCallback args_callback);
+  std::optional<CounterId> PushCounter(int64_t timestamp,
+                                       double value,
+                                       TrackId track_id,
+                                       SetArgsCallback args_callback);
 
   // Adds a counter event to the counters table for counter events which
   // should be associated with a process but only have a thread context
@@ -59,7 +59,7 @@
   //
   // This function will resolve the utid to a upid when the events are
   // flushed (see |FlushPendingEvents()|).
-  virtual base::Optional<CounterId> PushProcessCounterForThread(
+  virtual std::optional<CounterId> PushProcessCounterForThread(
       int64_t timestamp,
       double value,
       StringId name_id,
diff --git a/src/trace_processor/importers/common/flow_tracker.cc b/src/trace_processor/importers/common/flow_tracker.cc
index ff9f609..7805544 100644
--- a/src/trace_processor/importers/common/flow_tracker.cc
+++ b/src/trace_processor/importers/common/flow_tracker.cc
@@ -40,7 +40,7 @@
   it is a bit tricky to make it here.
   We suspect that this case is too rare or impossible */
 void FlowTracker::Begin(TrackId track_id, FlowId flow_id) {
-  base::Optional<SliceId> open_slice_id =
+  std::optional<SliceId> open_slice_id =
       context_->slice_tracker->GetTopmostSliceOnTrack(track_id);
   if (!open_slice_id) {
     context_->storage->IncrementStats(stats::flow_no_enclosing_slice);
@@ -58,7 +58,7 @@
 }
 
 void FlowTracker::Step(TrackId track_id, FlowId flow_id) {
-  base::Optional<SliceId> open_slice_id =
+  std::optional<SliceId> open_slice_id =
       context_->slice_tracker->GetTopmostSliceOnTrack(track_id);
   if (!open_slice_id) {
     context_->storage->IncrementStats(stats::flow_no_enclosing_slice);
@@ -86,7 +86,7 @@
     pending_flow_ids_map_[track_id].push_back(flow_id);
     return;
   }
-  base::Optional<SliceId> open_slice_id =
+  std::optional<SliceId> open_slice_id =
       context_->slice_tracker->GetTopmostSliceOnTrack(track_id);
   if (!open_slice_id) {
     context_->storage->IncrementStats(stats::flow_no_enclosing_slice);
diff --git a/src/trace_processor/importers/common/metadata_tracker.cc b/src/trace_processor/importers/common/metadata_tracker.cc
index 010b1b7..7468c42 100644
--- a/src/trace_processor/importers/common/metadata_tracker.cc
+++ b/src/trace_processor/importers/common/metadata_tracker.cc
@@ -52,7 +52,7 @@
 
   auto* metadata_table = storage_->mutable_metadata_table();
   uint32_t key_idx = static_cast<uint32_t>(key);
-  base::Optional<uint32_t> opt_row =
+  std::optional<uint32_t> opt_row =
       metadata_table->name().IndexOf(metadata::kNames[key_idx]);
   if (opt_row) {
     WriteValue(*opt_row, value);
diff --git a/src/trace_processor/importers/common/parser_types.h b/src/trace_processor/importers/common/parser_types.h
index e6575f1..2b5648a 100644
--- a/src/trace_processor/importers/common/parser_types.h
+++ b/src/trace_processor/importers/common/parser_types.h
@@ -68,8 +68,8 @@
   }
 
   TracePacketData trace_packet_data;
-  base::Optional<int64_t> thread_timestamp;
-  base::Optional<int64_t> thread_instruction_count;
+  std::optional<int64_t> thread_timestamp;
+  std::optional<int64_t> thread_instruction_count;
   double counter_value = 0;
   std::array<double, kMaxNumExtraCounters> extra_counter_values = {};
 };
diff --git a/src/trace_processor/importers/common/process_tracker.cc b/src/trace_processor/importers/common/process_tracker.cc
index df14d2c..473c48e 100644
--- a/src/trace_processor/importers/common/process_tracker.cc
+++ b/src/trace_processor/importers/common/process_tracker.cc
@@ -48,7 +48,7 @@
 
 ProcessTracker::~ProcessTracker() = default;
 
-UniqueTid ProcessTracker::StartNewThread(base::Optional<int64_t> timestamp,
+UniqueTid ProcessTracker::StartNewThread(std::optional<int64_t> timestamp,
                                          uint32_t tid) {
   tables::ThreadTable::Row row;
   row.tid = tid;
@@ -74,7 +74,7 @@
   // we would have already ended the process and we don't want to
   // create a new thread here (see b/193520421 for an example of a trace
   // where this happens in practice).
-  base::Optional<UniqueTid> opt_utid = GetThreadOrNull(tid);
+  std::optional<UniqueTid> opt_utid = GetThreadOrNull(tid);
   if (!opt_utid)
     return;
 
@@ -97,10 +97,10 @@
   pids_.Erase(tid);
 }
 
-base::Optional<UniqueTid> ProcessTracker::GetThreadOrNull(uint32_t tid) {
-  auto opt_utid = GetThreadOrNull(tid, base::nullopt);
+std::optional<UniqueTid> ProcessTracker::GetThreadOrNull(uint32_t tid) {
+  auto opt_utid = GetThreadOrNull(tid, std::nullopt);
   if (!opt_utid)
-    return base::nullopt;
+    return std::nullopt;
 
   auto* threads = context_->storage->mutable_thread_table();
   UniqueTid utid = *opt_utid;
@@ -117,7 +117,7 @@
 
 UniqueTid ProcessTracker::GetOrCreateThread(uint32_t tid) {
   auto utid = GetThreadOrNull(tid);
-  return utid ? *utid : StartNewThread(base::nullopt, tid);
+  return utid ? *utid : StartNewThread(std::nullopt, tid);
 }
 
 UniqueTid ProcessTracker::UpdateThreadName(uint32_t tid,
@@ -168,15 +168,15 @@
   return true;
 }
 
-base::Optional<UniqueTid> ProcessTracker::GetThreadOrNull(
+std::optional<UniqueTid> ProcessTracker::GetThreadOrNull(
     uint32_t tid,
-    base::Optional<uint32_t> pid) {
+    std::optional<uint32_t> pid) {
   auto* threads = context_->storage->mutable_thread_table();
   auto* processes = context_->storage->mutable_process_table();
 
   auto vector_it = tids_.Find(tid);
   if (!vector_it)
-    return base::nullopt;
+    return std::nullopt;
 
   // Iterate backwards through the threads so ones later in the trace are more
   // likely to be picked.
@@ -202,17 +202,17 @@
     if (!pid || current_pid == *pid)
       return current_utid;
   }
-  return base::nullopt;
+  return std::nullopt;
 }
 
 UniqueTid ProcessTracker::UpdateThread(uint32_t tid, uint32_t pid) {
   auto* thread_table = context_->storage->mutable_thread_table();
 
   // Try looking for a thread that matches both tid and thread group id (pid).
-  base::Optional<UniqueTid> opt_utid = GetThreadOrNull(tid, pid);
+  std::optional<UniqueTid> opt_utid = GetThreadOrNull(tid, pid);
 
   // If no matching thread was found, create a new one.
-  UniqueTid utid = opt_utid ? *opt_utid : StartNewThread(base::nullopt, tid);
+  UniqueTid utid = opt_utid ? *opt_utid : StartNewThread(std::nullopt, tid);
   PERFETTO_DCHECK(thread_table->tid()[utid] == tid);
 
   // Find matching process or create new one.
@@ -229,23 +229,23 @@
   trusted_pids_[uuid] = trusted_pid;
 }
 
-base::Optional<uint32_t> ProcessTracker::GetTrustedPid(uint64_t uuid) {
+std::optional<uint32_t> ProcessTracker::GetTrustedPid(uint64_t uuid) {
   if (trusted_pids_.find(uuid) == trusted_pids_.end())
-    return base::nullopt;
+    return std::nullopt;
   return trusted_pids_[uuid];
 }
 
-base::Optional<uint32_t> ProcessTracker::ResolveNamespacedTid(
+std::optional<uint32_t> ProcessTracker::ResolveNamespacedTid(
     uint32_t root_level_pid,
     uint32_t tid) {
   if (root_level_pid <= 0)  // Not a valid pid.
-    return base::nullopt;
+    return std::nullopt;
 
   // If the process doesn't run in a namespace (or traced_probes doesn't observe
-  // that), return base::nullopt as failure to resolve.
+  // that), return std::nullopt as failure to resolve.
   auto process_it = namespaced_processes_.find(root_level_pid);
   if (process_it == namespaced_processes_.end())
-    return base::nullopt;
+    return std::nullopt;
 
   // Check if it's the main thread.
   const auto& process = process_it->second;
@@ -264,11 +264,11 @@
   }
 
   // Failed to resolve or the thread isn't namespaced
-  return base::nullopt;
+  return std::nullopt;
 }
 
-UniquePid ProcessTracker::StartNewProcess(base::Optional<int64_t> timestamp,
-                                          base::Optional<uint32_t> parent_tid,
+UniquePid ProcessTracker::StartNewProcess(std::optional<int64_t> timestamp,
+                                          std::optional<uint32_t> parent_tid,
                                           uint32_t pid,
                                           StringId main_thread_name,
                                           ThreadNamePriority priority) {
@@ -311,10 +311,10 @@
 }
 
 UniquePid ProcessTracker::SetProcessMetadata(uint32_t pid,
-                                             base::Optional<uint32_t> ppid,
+                                             std::optional<uint32_t> ppid,
                                              base::StringView name,
                                              base::StringView cmdline) {
-  base::Optional<UniquePid> pupid;
+  std::optional<UniquePid> pupid;
   if (ppid.has_value()) {
     pupid = GetOrCreateProcess(ppid.value());
   }
@@ -363,7 +363,7 @@
   auto* process_table = context_->storage->mutable_process_table();
 
   UniqueTid utid = UpdateThreadName(tid, thread_name, priority);
-  base::Optional<UniquePid> opt_upid = thread_table->upid()[utid];
+  std::optional<UniquePid> opt_upid = thread_table->upid()[utid];
   if (opt_upid.has_value() && process_table->pid()[*opt_upid] == tid) {
     PERFETTO_DCHECK(thread_table->is_main_thread()[utid]);
     process_table->mutable_name()->Set(*opt_upid, thread_name);
diff --git a/src/trace_processor/importers/common/process_tracker.h b/src/trace_processor/importers/common/process_tracker.h
index 37298a6..df648c2 100644
--- a/src/trace_processor/importers/common/process_tracker.h
+++ b/src/trace_processor/importers/common/process_tracker.h
@@ -62,7 +62,7 @@
 
   // Called when a task_newtask is observed. This force the tracker to start
   // a new UTID for the thread, which is needed for TID-recycling resolution.
-  UniqueTid StartNewThread(base::Optional<int64_t> timestamp, uint32_t tid);
+  UniqueTid StartNewThread(std::optional<int64_t> timestamp, uint32_t tid);
 
   // Returns whether a thread is considered alive by the process tracker.
   bool IsThreadAlive(UniqueTid utid);
@@ -71,8 +71,8 @@
   // end the thread lifetime for the utid associated with the given tid.
   void EndThread(int64_t timestamp, uint32_t tid);
 
-  // Returns the thread utid or base::nullopt if it doesn't exist.
-  base::Optional<UniqueTid> GetThreadOrNull(uint32_t tid);
+  // Returns the thread utid or std::nullopt if it doesn't exist.
+  std::optional<UniqueTid> GetThreadOrNull(uint32_t tid);
 
   // Returns the thread utid (or creates a new entry if not present)
   UniqueTid GetOrCreateThread(uint32_t tid);
@@ -97,23 +97,23 @@
   // Associates trusted_pid with track UUID.
   void UpdateTrustedPid(uint32_t trusted_pid, uint64_t uuid);
 
-  // Returns the trusted_pid associated with the track UUID, or base::nullopt if
+  // Returns the trusted_pid associated with the track UUID, or std::nullopt if
   // not found.
-  base::Optional<uint32_t> GetTrustedPid(uint64_t uuid);
+  std::optional<uint32_t> GetTrustedPid(uint64_t uuid);
 
   // Performs namespace-local to root-level resolution of thread or process id,
   // given tid (can be root-level or namespace-local, but we don't know
   // beforehand) and root-level pid/tgid that the thread belongs to.
   // Returns the root-level thread id for tid on successful resolution;
-  // otherwise, returns base::nullopt on resolution failure, or the thread of
+  // otherwise, returns std::nullopt on resolution failure, or the thread of
   // tid isn't running in a pid namespace.
-  base::Optional<uint32_t> ResolveNamespacedTid(uint32_t root_level_pid,
-                                                uint32_t tid);
+  std::optional<uint32_t> ResolveNamespacedTid(uint32_t root_level_pid,
+                                               uint32_t tid);
 
   // Called when a task_newtask without the CLONE_THREAD flag is observed.
   // This force the tracker to start both a new UTID and a new UPID.
-  UniquePid StartNewProcess(base::Optional<int64_t> timestamp,
-                            base::Optional<uint32_t> parent_tid,
+  UniquePid StartNewProcess(std::optional<int64_t> timestamp,
+                            std::optional<uint32_t> parent_tid,
                             uint32_t pid,
                             StringId main_thread_name,
                             ThreadNamePriority priority);
@@ -122,7 +122,7 @@
   // for that pid or assigns a new one.
   // Virtual for testing.
   virtual UniquePid SetProcessMetadata(uint32_t pid,
-                                       base::Optional<uint32_t> ppid,
+                                       std::optional<uint32_t> ppid,
                                        base::StringView name,
                                        base::StringView cmdline);
 
@@ -149,9 +149,9 @@
   virtual UniquePid GetOrCreateProcess(uint32_t pid);
 
   // Returns the upid for a given pid.
-  base::Optional<UniquePid> UpidForPidForTesting(uint32_t pid) {
+  std::optional<UniquePid> UpidForPidForTesting(uint32_t pid) {
     auto it = pids_.Find(pid);
-    return it ? base::make_optional(*it) : base::nullopt;
+    return it ? std::make_optional(*it) : std::nullopt;
   }
 
   // Returns the bounds of a range that includes all UniqueTids that have the
@@ -191,10 +191,10 @@
 
  private:
   // Returns the utid of a thread having |tid| and |pid| as the parent process.
-  // pid == base::nullopt matches all processes.
-  // Returns base::nullopt if such a thread doesn't exist.
-  base::Optional<uint32_t> GetThreadOrNull(uint32_t tid,
-                                           base::Optional<uint32_t> pid);
+  // pid == std::nullopt matches all processes.
+  // Returns std::nullopt if such a thread doesn't exist.
+  std::optional<uint32_t> GetThreadOrNull(uint32_t tid,
+                                          std::optional<uint32_t> pid);
 
   // Called whenever we discover that the passed thread belongs to the passed
   // process. The |pending_assocs_| vector is scanned to see if there are any
diff --git a/src/trace_processor/importers/common/process_tracker_unittest.cc b/src/trace_processor/importers/common/process_tracker_unittest.cc
index de4102e..e4f06ce 100644
--- a/src/trace_processor/importers/common/process_tracker_unittest.cc
+++ b/src/trace_processor/importers/common/process_tracker_unittest.cc
@@ -16,8 +16,9 @@
 
 #include "src/trace_processor/importers/common/process_tracker.h"
 
+#include <optional>
+
 #include "perfetto/base/logging.h"
-#include "perfetto/ext/base/optional.h"
 #include "src/trace_processor/importers/common/args_tracker.h"
 #include "src/trace_processor/importers/common/event_tracker.h"
 #include "test/gtest_and_gmock.h"
@@ -46,7 +47,7 @@
 };
 
 TEST_F(ProcessTrackerTest, PushProcess) {
-  context.process_tracker->SetProcessMetadata(1, base::nullopt, "test",
+  context.process_tracker->SetProcessMetadata(1, std::nullopt, "test",
                                               base::StringView());
   auto opt_upid = context.process_tracker->UpidForPidForTesting(1);
   ASSERT_EQ(opt_upid.value_or(-1), 1u);
@@ -65,18 +66,18 @@
 }
 
 TEST_F(ProcessTrackerTest, PushTwoProcessEntries_SamePidAndName) {
-  context.process_tracker->SetProcessMetadata(1, base::nullopt, "test",
+  context.process_tracker->SetProcessMetadata(1, std::nullopt, "test",
                                               base::StringView());
-  context.process_tracker->SetProcessMetadata(1, base::nullopt, "test",
+  context.process_tracker->SetProcessMetadata(1, std::nullopt, "test",
                                               base::StringView());
   auto opt_upid = context.process_tracker->UpidForPidForTesting(1);
   ASSERT_EQ(opt_upid.value_or(-1), 1u);
 }
 
 TEST_F(ProcessTrackerTest, PushTwoProcessEntries_DifferentPid) {
-  context.process_tracker->SetProcessMetadata(1, base::nullopt, "test",
+  context.process_tracker->SetProcessMetadata(1, std::nullopt, "test",
                                               base::StringView());
-  context.process_tracker->SetProcessMetadata(3, base::nullopt, "test",
+  context.process_tracker->SetProcessMetadata(3, std::nullopt, "test",
                                               base::StringView());
   auto opt_upid = context.process_tracker->UpidForPidForTesting(1);
   ASSERT_EQ(opt_upid.value_or(-1), 1u);
@@ -85,7 +86,7 @@
 }
 
 TEST_F(ProcessTrackerTest, AddProcessEntry_CorrectName) {
-  context.process_tracker->SetProcessMetadata(1, base::nullopt, "test",
+  context.process_tracker->SetProcessMetadata(1, std::nullopt, "test",
                                               base::StringView());
   auto name = context.storage->process_table().name().GetString(1);
 
@@ -108,12 +109,12 @@
 
 TEST_F(ProcessTrackerTest, PidReuseWithoutStartAndEndThread) {
   UniquePid p1 = context.process_tracker->StartNewProcess(
-      base::nullopt, base::nullopt, /*pid=*/1, kNullStringId,
+      std::nullopt, std::nullopt, /*pid=*/1, kNullStringId,
       ThreadNamePriority::kFtrace);
   UniqueTid t1 = context.process_tracker->UpdateThread(/*tid=*/2, /*pid=*/1);
 
   UniquePid p2 = context.process_tracker->StartNewProcess(
-      base::nullopt, base::nullopt, /*pid=*/1, kNullStringId,
+      std::nullopt, std::nullopt, /*pid=*/1, kNullStringId,
       ThreadNamePriority::kFtrace);
   UniqueTid t2 = context.process_tracker->UpdateThread(/*tid=*/2, /*pid=*/1);
 
@@ -128,7 +129,7 @@
 
 TEST_F(ProcessTrackerTest, Cmdline) {
   UniquePid upid = context.process_tracker->SetProcessMetadata(
-      1, base::nullopt, "test", "cmdline blah");
+      1, std::nullopt, "test", "cmdline blah");
   ASSERT_EQ(context.storage->process_table().cmdline().GetString(upid),
             "cmdline blah");
 }
@@ -158,7 +159,7 @@
 
 TEST_F(ProcessTrackerTest, SetStartTsIfUnset) {
   auto upid = context.process_tracker->StartNewProcess(
-      /*timestamp=*/base::nullopt, 0u, 123, kNullStringId,
+      /*timestamp=*/std::nullopt, 0u, 123, kNullStringId,
       ThreadNamePriority::kFtrace);
   context.process_tracker->SetStartTsIfUnset(upid, 1000);
   ASSERT_EQ(context.storage->process_table().start_ts()[upid], 1000);
@@ -188,7 +189,7 @@
 
 TEST_F(ProcessTrackerTest, EndThreadAfterProcessEnd) {
   context.process_tracker->StartNewProcess(
-      100, base::nullopt, 123, kNullStringId, ThreadNamePriority::kFtrace);
+      100, std::nullopt, 123, kNullStringId, ThreadNamePriority::kFtrace);
   context.process_tracker->UpdateThread(124, 123);
 
   context.process_tracker->EndThread(200, 123);
@@ -237,7 +238,7 @@
 
   // Don't resolve if the process/thread isn't namespaced.
   ASSERT_EQ(context.process_tracker->ResolveNamespacedTid(2001, 2002),
-            base::nullopt);
+            std::nullopt);
 
   // Resolve from namespace-local PID to root-level PID.
   ASSERT_EQ(context.process_tracker->ResolveNamespacedTid(1001, 1).value(),
diff --git a/src/trace_processor/importers/common/slice_tracker.cc b/src/trace_processor/importers/common/slice_tracker.cc
index 9f93fac..5f3af01 100644
--- a/src/trace_processor/importers/common/slice_tracker.cc
+++ b/src/trace_processor/importers/common/slice_tracker.cc
@@ -38,11 +38,11 @@
 
 SliceTracker::~SliceTracker() = default;
 
-base::Optional<SliceId> SliceTracker::Begin(int64_t timestamp,
-                                            TrackId track_id,
-                                            StringId category,
-                                            StringId raw_name,
-                                            SetArgsCallback args_callback) {
+std::optional<SliceId> SliceTracker::Begin(int64_t timestamp,
+                                           TrackId track_id,
+                                           StringId category,
+                                           StringId raw_name,
+                                           SetArgsCallback args_callback) {
   const StringId name =
       context_->slice_translation_table->TranslateName(raw_name);
   tables::SliceTable::Row row(timestamp, kPendingDuration, track_id, category,
@@ -77,12 +77,12 @@
   });
 }
 
-base::Optional<SliceId> SliceTracker::Scoped(int64_t timestamp,
-                                             TrackId track_id,
-                                             StringId category,
-                                             StringId raw_name,
-                                             int64_t duration,
-                                             SetArgsCallback args_callback) {
+std::optional<SliceId> SliceTracker::Scoped(int64_t timestamp,
+                                            TrackId track_id,
+                                            StringId category,
+                                            StringId raw_name,
+                                            int64_t duration,
+                                            SetArgsCallback args_callback) {
   PERFETTO_DCHECK(duration >= 0);
 
   const StringId name =
@@ -93,11 +93,11 @@
   });
 }
 
-base::Optional<SliceId> SliceTracker::End(int64_t timestamp,
-                                          TrackId track_id,
-                                          StringId category,
-                                          StringId raw_name,
-                                          SetArgsCallback args_callback) {
+std::optional<SliceId> SliceTracker::End(int64_t timestamp,
+                                         TrackId track_id,
+                                         StringId category,
+                                         StringId raw_name,
+                                         SetArgsCallback args_callback) {
   const StringId name =
       context_->slice_translation_table->TranslateName(raw_name);
   auto finder = [this, category, name](const SlicesStack& stack) {
@@ -106,23 +106,23 @@
   return CompleteSlice(timestamp, track_id, args_callback, finder);
 }
 
-base::Optional<uint32_t> SliceTracker::AddArgs(TrackId track_id,
-                                               StringId category,
-                                               StringId name,
-                                               SetArgsCallback args_callback) {
+std::optional<uint32_t> SliceTracker::AddArgs(TrackId track_id,
+                                              StringId category,
+                                              StringId name,
+                                              SetArgsCallback args_callback) {
   auto* it = stacks_.Find(track_id);
   if (!it)
-    return base::nullopt;
+    return std::nullopt;
 
   auto& stack = it->slice_stack;
   if (stack.empty())
-    return base::nullopt;
+    return std::nullopt;
 
   auto* slices = context_->storage->mutable_slice_table();
-  base::Optional<uint32_t> stack_idx =
+  std::optional<uint32_t> stack_idx =
       MatchingIncompleteSliceIndex(stack, name, category);
   if (!stack_idx.has_value())
-    return base::nullopt;
+    return std::nullopt;
 
   tables::SliceTable::RowNumber num = stack[*stack_idx].row;
   tables::SliceTable::RowReference ref = num.ToRowReference(slices);
@@ -135,7 +135,7 @@
   return num.row_number();
 }
 
-base::Optional<SliceId> SliceTracker::StartSlice(
+std::optional<SliceId> SliceTracker::StartSlice(
     int64_t timestamp,
     TrackId track_id,
     SetArgsCallback args_callback,
@@ -143,7 +143,7 @@
   // At this stage all events should be globally timestamp ordered.
   if (timestamp < prev_timestamp_) {
     context_->storage->IncrementStats(stats::slice_out_of_order);
-    return base::nullopt;
+    return std::nullopt;
   }
   prev_timestamp_ = timestamp;
 
@@ -159,7 +159,7 @@
     // If this is an unnestable track, don't start a new slice if one already
     // exists.
     if (!stack.empty()) {
-      return base::nullopt;
+      return std::nullopt;
     }
   }
 
@@ -168,12 +168,12 @@
 
   size_t depth = stack.size();
 
-  base::Optional<tables::SliceTable::RowReference> parent_ref =
-      depth == 0 ? base::nullopt
-                 : base::make_optional(stack.back().row.ToRowReference(slices));
+  std::optional<tables::SliceTable::RowReference> parent_ref =
+      depth == 0 ? std::nullopt
+                 : std::make_optional(stack.back().row.ToRowReference(slices));
   int64_t parent_stack_id = parent_ref ? parent_ref->stack_id() : 0;
-  base::Optional<tables::SliceTable::Id> parent_id =
-      parent_ref ? base::make_optional(parent_ref->id()) : base::nullopt;
+  std::optional<tables::SliceTable::Id> parent_id =
+      parent_ref ? std::make_optional(parent_ref->id()) : std::nullopt;
 
   SliceId id = inserter();
   tables::SliceTable::RowReference ref = *slices->FindById(id);
@@ -185,7 +185,7 @@
     PERFETTO_DLOG("Last slice: %s", parent_name.c_str());
     PERFETTO_DLOG("Current slice: %s", name.c_str());
     PERFETTO_DFATAL("Slices with too large depth found.");
-    return base::nullopt;
+    return std::nullopt;
   }
   StackPush(track_id, ref);
 
@@ -204,35 +204,35 @@
   return id;
 }
 
-base::Optional<SliceId> SliceTracker::CompleteSlice(
+std::optional<SliceId> SliceTracker::CompleteSlice(
     int64_t timestamp,
     TrackId track_id,
     SetArgsCallback args_callback,
-    std::function<base::Optional<uint32_t>(const SlicesStack&)> finder) {
+    std::function<std::optional<uint32_t>(const SlicesStack&)> finder) {
   // At this stage all events should be globally timestamp ordered.
   if (timestamp < prev_timestamp_) {
     context_->storage->IncrementStats(stats::slice_out_of_order);
-    return base::nullopt;
+    return std::nullopt;
   }
   prev_timestamp_ = timestamp;
 
   auto it = stacks_.Find(track_id);
   if (!it)
-    return base::nullopt;
+    return std::nullopt;
 
   TrackInfo& track_info = *it;
   SlicesStack& stack = track_info.slice_stack;
   MaybeCloseStack(timestamp, stack, track_id);
   if (stack.empty())
-    return base::nullopt;
+    return std::nullopt;
 
   auto* slices = context_->storage->mutable_slice_table();
-  base::Optional<uint32_t> stack_idx = finder(stack);
+  std::optional<uint32_t> stack_idx = finder(stack);
 
   // If we are trying to close slices that are not open on the stack (e.g.,
   // slices that began before tracing started), bail out.
   if (!stack_idx)
-    return base::nullopt;
+    return std::nullopt;
 
   const auto& slice_info = stack[stack_idx.value()];
 
@@ -266,8 +266,8 @@
 
 // Returns the first incomplete slice in the stack with matching name and
 // category. We assume null category/name matches everything. Returns
-// nullopt if no matching slice is found.
-base::Optional<uint32_t> SliceTracker::MatchingIncompleteSliceIndex(
+// std::nullopt if no matching slice is found.
+std::optional<uint32_t> SliceTracker::MatchingIncompleteSliceIndex(
     const SlicesStack& stack,
     StringId name,
     StringId category) {
@@ -277,19 +277,19 @@
         stack[static_cast<size_t>(i)].row.ToRowReference(slices);
     if (ref.dur() != kPendingDuration)
       continue;
-    base::Optional<StringId> other_category = ref.category();
+    std::optional<StringId> other_category = ref.category();
     if (!category.is_null() && (!other_category || other_category->is_null() ||
                                 category != other_category)) {
       continue;
     }
-    base::Optional<StringId> other_name = ref.name();
+    std::optional<StringId> other_name = ref.name();
     if (!name.is_null() && other_name && !other_name->is_null() &&
         name != other_name) {
       continue;
     }
     return static_cast<uint32_t>(i);
   }
-  return base::nullopt;
+  return std::nullopt;
 }
 
 void SliceTracker::MaybeAddTranslatableArgs(SliceInfo& slice_info) {
@@ -339,14 +339,14 @@
   on_slice_begin_callback_ = callback;
 }
 
-base::Optional<SliceId> SliceTracker::GetTopmostSliceOnTrack(
+std::optional<SliceId> SliceTracker::GetTopmostSliceOnTrack(
     TrackId track_id) const {
   const auto* iter = stacks_.Find(track_id);
   if (!iter)
-    return base::nullopt;
+    return std::nullopt;
   const auto& stack = iter->slice_stack;
   if (stack.empty())
-    return base::nullopt;
+    return std::nullopt;
   const auto& slice = context_->storage->slice_table();
   return stack.back().row.ToRowReference(slice).id();
 }
diff --git a/src/trace_processor/importers/common/slice_tracker.h b/src/trace_processor/importers/common/slice_tracker.h
index b2bdebd..f0f59da 100644
--- a/src/trace_processor/importers/common/slice_tracker.h
+++ b/src/trace_processor/importers/common/slice_tracker.h
@@ -39,7 +39,7 @@
   virtual ~SliceTracker();
 
   // virtual for testing
-  virtual base::Optional<SliceId> Begin(
+  virtual std::optional<SliceId> Begin(
       int64_t timestamp,
       TrackId track_id,
       StringId category,
@@ -56,7 +56,7 @@
                              SetArgsCallback args_callback);
 
   template <typename Table>
-  base::Optional<SliceId> BeginTyped(
+  std::optional<SliceId> BeginTyped(
       Table* table,
       typename Table::Row row,
       SetArgsCallback args_callback = SetArgsCallback()) {
@@ -70,7 +70,7 @@
   }
 
   // virtual for testing
-  virtual base::Optional<SliceId> Scoped(
+  virtual std::optional<SliceId> Scoped(
       int64_t timestamp,
       TrackId track_id,
       StringId category,
@@ -79,7 +79,7 @@
       SetArgsCallback args_callback = SetArgsCallback());
 
   template <typename Table>
-  base::Optional<SliceId> ScopedTyped(
+  std::optional<SliceId> ScopedTyped(
       Table* table,
       typename Table::Row row,
       SetArgsCallback args_callback = SetArgsCallback()) {
@@ -92,7 +92,7 @@
   }
 
   // virtual for testing
-  virtual base::Optional<SliceId> End(
+  virtual std::optional<SliceId> End(
       int64_t timestamp,
       TrackId track_id,
       StringId opt_category = {},
@@ -102,16 +102,16 @@
   // Usually args should be added in the Begin or End args_callback but this
   // method is for the situation where new args need to be added to an
   // in-progress slice.
-  base::Optional<uint32_t> AddArgs(TrackId track_id,
-                                   StringId category,
-                                   StringId name,
-                                   SetArgsCallback args_callback);
+  std::optional<uint32_t> AddArgs(TrackId track_id,
+                                  StringId category,
+                                  StringId name,
+                                  SetArgsCallback args_callback);
 
   void FlushPendingSlices();
 
   void SetOnSliceBeginCallback(OnSliceBeginCallback callback);
 
-  base::Optional<SliceId> GetTopmostSliceOnTrack(TrackId track_id) const;
+  std::optional<SliceId> GetTopmostSliceOnTrack(TrackId track_id) const;
 
  private:
   // Slices which have been opened but haven't been closed yet will be marked
@@ -141,23 +141,22 @@
   };
 
   // virtual for testing.
-  virtual base::Optional<SliceId> StartSlice(int64_t timestamp,
-                                             TrackId track_id,
-                                             SetArgsCallback args_callback,
-                                             std::function<SliceId()> inserter);
+  virtual std::optional<SliceId> StartSlice(int64_t timestamp,
+                                            TrackId track_id,
+                                            SetArgsCallback args_callback,
+                                            std::function<SliceId()> inserter);
 
-  base::Optional<SliceId> CompleteSlice(
+  std::optional<SliceId> CompleteSlice(
       int64_t timestamp,
       TrackId track_id,
       SetArgsCallback args_callback,
-      std::function<base::Optional<uint32_t>(const SlicesStack&)> finder);
+      std::function<std::optional<uint32_t>(const SlicesStack&)> finder);
 
   void MaybeCloseStack(int64_t end_ts, const SlicesStack&, TrackId track_id);
 
-  base::Optional<uint32_t> MatchingIncompleteSliceIndex(
-      const SlicesStack& stack,
-      StringId name,
-      StringId category);
+  std::optional<uint32_t> MatchingIncompleteSliceIndex(const SlicesStack& stack,
+                                                       StringId name,
+                                                       StringId category);
 
   int64_t GetStackHash(const SlicesStack&);
 
diff --git a/src/trace_processor/importers/common/slice_tracker_unittest.cc b/src/trace_processor/importers/common/slice_tracker_unittest.cc
index 5b8d37d..ab214eb 100644
--- a/src/trace_processor/importers/common/slice_tracker_unittest.cc
+++ b/src/trace_processor/importers/common/slice_tracker_unittest.cc
@@ -322,7 +322,7 @@
   SliceId parent = context.storage->slice_table().id()[0];
   SliceId child = context.storage->slice_table().id()[1];
   EXPECT_THAT(context.storage->slice_table().parent_id().ToVectorForTesting(),
-              ElementsAre(base::nullopt, parent, child));
+              ElementsAre(std::nullopt, parent, child));
 }
 
 TEST(SliceTrackerTest, IgnoreMismatchedEnds) {
@@ -460,7 +460,7 @@
   TrackId track{1u};
   TrackId track2{2u};
 
-  EXPECT_EQ(tracker.GetTopmostSliceOnTrack(track), base::nullopt);
+  EXPECT_EQ(tracker.GetTopmostSliceOnTrack(track), std::nullopt);
 
   tracker.Begin(100, track, StringId::Raw(11), StringId::Raw(11));
   SliceId slice1 = context.storage->slice_table().id()[0];
@@ -472,7 +472,7 @@
 
   EXPECT_EQ(tracker.GetTopmostSliceOnTrack(track).value(), slice2);
 
-  EXPECT_EQ(tracker.GetTopmostSliceOnTrack(track2), base::nullopt);
+  EXPECT_EQ(tracker.GetTopmostSliceOnTrack(track2), std::nullopt);
 
   tracker.End(140, track, StringId::Raw(22), StringId::Raw(22));
 
@@ -480,7 +480,7 @@
 
   tracker.End(330, track, StringId::Raw(11), StringId::Raw(11));
 
-  EXPECT_EQ(tracker.GetTopmostSliceOnTrack(track), base::nullopt);
+  EXPECT_EQ(tracker.GetTopmostSliceOnTrack(track), std::nullopt);
 }
 
 TEST(SliceTrackerTest, OnSliceBeginCallback) {
diff --git a/src/trace_processor/importers/common/system_info_tracker.cc b/src/trace_processor/importers/common/system_info_tracker.cc
index f8c1007..89f0baf 100644
--- a/src/trace_processor/importers/common/system_info_tracker.cc
+++ b/src/trace_processor/importers/common/system_info_tracker.cc
@@ -26,7 +26,7 @@
 void SystemInfoTracker::SetKernelVersion(base::StringView name,
                                          base::StringView release) {
   if (name.empty() || release.empty() || name != "Linux") {
-    version_ = base::nullopt;
+    version_ = std::nullopt;
     return;
   }
 
diff --git a/src/trace_processor/importers/common/system_info_tracker.h b/src/trace_processor/importers/common/system_info_tracker.h
index af108c5..d9544d1 100644
--- a/src/trace_processor/importers/common/system_info_tracker.h
+++ b/src/trace_processor/importers/common/system_info_tracker.h
@@ -17,7 +17,8 @@
 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_SYSTEM_INFO_TRACKER_H_
 #define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_SYSTEM_INFO_TRACKER_H_
 
-#include "perfetto/ext/base/optional.h"
+#include <optional>
+
 #include "perfetto/ext/base/string_view.h"
 #include "src/trace_processor/types/destructible.h"
 #include "src/trace_processor/types/trace_processor_context.h"
@@ -42,12 +43,12 @@
 
   void SetKernelVersion(base::StringView name, base::StringView release);
 
-  base::Optional<VersionNumber> GetKernelVersion() { return version_; }
+  std::optional<VersionNumber> GetKernelVersion() { return version_; }
 
  private:
   explicit SystemInfoTracker();
 
-  base::Optional<VersionNumber> version_;
+  std::optional<VersionNumber> version_;
 };
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/common/track_tracker.h b/src/trace_processor/importers/common/track_tracker.h
index f172148..c4a4232 100644
--- a/src/trace_processor/importers/common/track_tracker.h
+++ b/src/trace_processor/importers/common/track_tracker.h
@@ -145,7 +145,7 @@
     }
   };
   struct ChromeTrackTuple {
-    base::Optional<int64_t> upid;
+    std::optional<int64_t> upid;
     int64_t source_id = 0;
     StringId source_scope = StringId::Null();
 
@@ -178,8 +178,8 @@
   std::map<std::pair<StringId, int32_t>, TrackId>
       energy_per_uid_counter_tracks_;
 
-  base::Optional<TrackId> chrome_global_instant_track_id_;
-  base::Optional<TrackId> trigger_track_id_;
+  std::optional<TrackId> chrome_global_instant_track_id_;
+  std::optional<TrackId> trigger_track_id_;
 
   const StringId source_key_ = kNullStringId;
   const StringId source_id_key_ = kNullStringId;
diff --git a/src/trace_processor/importers/ftrace/binder_tracker.cc b/src/trace_processor/importers/ftrace/binder_tracker.cc
index 5e95579..7133c1f 100644
--- a/src/trace_processor/importers/ftrace/binder_tracker.cc
+++ b/src/trace_processor/importers/ftrace/binder_tracker.cc
@@ -170,7 +170,7 @@
     return;
   }
 
-  base::Optional<SliceId> recv_slice_id;
+  std::optional<SliceId> recv_slice_id;
   if (transaction.is_oneway) {
     recv_slice_id = context_->slice_tracker->Scoped(
         ts, track_id, binder_category_id_, async_rcv_id_, 0,
diff --git a/src/trace_processor/importers/ftrace/binder_tracker.h b/src/trace_processor/importers/ftrace/binder_tracker.h
index aa710e3..b17990a 100644
--- a/src/trace_processor/importers/ftrace/binder_tracker.h
+++ b/src/trace_processor/importers/ftrace/binder_tracker.h
@@ -18,10 +18,10 @@
 #define SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_BINDER_TRACKER_H_
 
 #include <stdint.h>
+#include <optional>
 
 #include "perfetto/base/flat_set.h"
 #include "perfetto/ext/base/flat_hash_map.h"
-#include "perfetto/ext/base/optional.h"
 #include "src/trace_processor/importers/common/args_tracker.h"
 #include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/types/destructible.h"
@@ -72,8 +72,8 @@
     bool is_reply = false;
     bool is_oneway = false;
     SetArgsCallback args_inserter;
-    base::Optional<TrackId> send_track_id;
-    base::Optional<SliceId> send_slice_id;
+    std::optional<TrackId> send_track_id;
+    std::optional<SliceId> send_slice_id;
   };
 
   TraceProcessorContext* const context_;
diff --git a/src/trace_processor/importers/ftrace/drm_tracker.cc b/src/trace_processor/importers/ftrace/drm_tracker.cc
index ebe5b31..003b827 100644
--- a/src/trace_processor/importers/ftrace/drm_tracker.cc
+++ b/src/trace_processor/importers/ftrace/drm_tracker.cc
@@ -184,7 +184,7 @@
     inserter->AddArg(sched_arg_job_id_, Variadic::UnsignedInteger(job_id));
   };
 
-  base::Optional<SliceId> slice_id =
+  std::optional<SliceId> slice_id =
       context_->slice_tracker->Begin(timestamp, ring.track_id, kNullStringId,
                                      sched_slice_job_id_, args_inserter);
 
@@ -210,7 +210,7 @@
     inserter->AddArg(sched_arg_job_id_, Variadic::UnsignedInteger(job_id));
   };
 
-  base::Optional<SliceId> slice_id = context_->slice_tracker->Scoped(
+  std::optional<SliceId> slice_id = context_->slice_tracker->Scoped(
       timestamp, track_id, kNullStringId, sched_slice_schedule_id_, 0,
       args_inserter);
 
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.cc b/src/trace_processor/importers/ftrace/ftrace_parser.cc
index 78157d1..29af4bc 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.cc
@@ -1079,7 +1079,7 @@
               ? metadata::all_data_source_started_ns
               : metadata::tracing_started_ns;
       const auto& metadata = context_->storage->metadata_table();
-      base::Optional<uint32_t> opt_row =
+      std::optional<uint32_t> opt_row =
           metadata.name().IndexOf(metadata::kNames[event_key]);
       if (opt_row) {
         drop_ftrace_data_before_ts_ = *metadata.int_value()[*opt_row];
@@ -1304,7 +1304,7 @@
   // [wr][atrace_slice..]
   // ...............[wri]
   //
-  base::Optional<UniqueTid> opt_utid =
+  std::optional<UniqueTid> opt_utid =
       context_->process_tracker->GetThreadOrNull(pid);
   if (opt_utid) {
     SyscallTracker::GetOrCreate(context_)->MaybeTruncateOngoingWriteSlice(
@@ -1842,7 +1842,7 @@
 }
 
 void FtraceParser::ParseCmaAllocStart(int64_t timestamp, uint32_t pid) {
-  base::Optional<VersionNumber> kernel_version =
+  std::optional<VersionNumber> kernel_version =
       SystemInfoTracker::GetOrCreate(context_)->GetKernelVersion();
   // CmaAllocInfo event only exists after 5.10
   if (kernel_version < VersionNumber{5, 10})
@@ -1858,7 +1858,7 @@
 void FtraceParser::ParseCmaAllocInfo(int64_t timestamp,
                                      uint32_t pid,
                                      ConstBytes blob) {
-  base::Optional<VersionNumber> kernel_version =
+  std::optional<VersionNumber> kernel_version =
       SystemInfoTracker::GetOrCreate(context_)->GetKernelVersion();
   // CmaAllocInfo event only exists after 5.10
   if (kernel_version < VersionNumber{5, 10})
@@ -2098,7 +2098,7 @@
     // the event as otherwise we would create fake processes which we
     // definitely want to avoid.
     // See b/192274404 for more info.
-    base::Optional<UniqueTid> opt_utid =
+    std::optional<UniqueTid> opt_utid =
         context_->process_tracker->GetThreadOrNull(pid);
     if (!opt_utid)
       return;
@@ -2156,7 +2156,7 @@
       protos::pbzero::InternedData::kKernelSymbolsFieldNumber,
       protos::pbzero::InternedString>(caller_iid);
 
-  base::Optional<StringId> blocked_function_str_id = base::nullopt;
+  std::optional<StringId> blocked_function_str_id = std::nullopt;
   if (interned_string) {
     protozero::ConstBytes str = interned_string->str();
     blocked_function_str_id = context_->storage->InternString(
@@ -2226,7 +2226,7 @@
 
   uint64_t nic_received_kilobytes = nic_received_bytes_[name] / 1024;
   TrackId track = context_->track_tracker->InternGlobalCounterTrack(name);
-  base::Optional<CounterId> id = context_->event_tracker->PushCounter(
+  std::optional<CounterId> id = context_->event_tracker->PushCounter(
       timestamp, static_cast<double>(nic_received_kilobytes), track);
   if (!id) {
     return;
@@ -2257,7 +2257,7 @@
 
   uint64_t nic_transmitted_kilobytes = nic_transmitted_bytes_[name] / 1024;
   TrackId track = context_->track_tracker->InternGlobalCounterTrack(name);
-  base::Optional<CounterId> id = context_->event_tracker->PushCounter(
+  std::optional<CounterId> id = context_->event_tracker->PushCounter(
       timestamp, static_cast<double>(nic_transmitted_kilobytes), track);
   if (!id) {
     return;
@@ -2416,7 +2416,7 @@
 
   TrackId track =
       context_->track_tracker->InternGlobalCounterTrack(kfree_skb_name_id_);
-  base::Optional<CounterId> id = context_->event_tracker->PushCounter(
+  std::optional<CounterId> id = context_->event_tracker->PushCounter(
       timestamp, static_cast<double>(num_of_kfree_skb_ip_prot), track);
   if (!id) {
     return;
diff --git a/src/trace_processor/importers/ftrace/rss_stat_tracker.cc b/src/trace_processor/importers/ftrace/rss_stat_tracker.cc
index 80ca10f..faebf94 100644
--- a/src/trace_processor/importers/ftrace/rss_stat_tracker.cc
+++ b/src/trace_processor/importers/ftrace/rss_stat_tracker.cc
@@ -47,8 +47,8 @@
                                   ConstBytes blob) {
   uint32_t member;
   int64_t size;
-  base::Optional<bool> curr;
-  base::Optional<int64_t> mm_id;
+  std::optional<bool> curr;
+  std::optional<int64_t> mm_id;
 
   if (field_id == FtraceEvent::kRssStatFieldNumber) {
     protos::pbzero::RssStatFtraceEvent::Decoder rss(blob.data, blob.size);
@@ -56,10 +56,10 @@
     member = static_cast<uint32_t>(rss.member());
     size = rss.size();
     if (rss.has_curr()) {
-      curr = base::make_optional(static_cast<bool>(rss.curr()));
+      curr = std::make_optional(static_cast<bool>(rss.curr()));
     }
     if (rss.has_mm_id()) {
-      mm_id = base::make_optional(rss.mm_id());
+      mm_id = std::make_optional(rss.mm_id());
     }
 
     ParseRssStat(ts, pid, size, member, curr, mm_id);
@@ -69,8 +69,8 @@
 
     member = static_cast<uint32_t>(rss.member());
     size = rss.size();
-    curr = base::make_optional(static_cast<bool>(rss.curr()));
-    mm_id = base::make_optional(rss.mm_id());
+    curr = std::make_optional(static_cast<bool>(rss.curr()));
+    mm_id = std::make_optional(rss.mm_id());
 
     ParseRssStat(ts, pid, size, member, curr, mm_id);
   } else {
@@ -82,8 +82,8 @@
                                   uint32_t pid,
                                   int64_t size,
                                   uint32_t member,
-                                  base::Optional<bool> curr,
-                                  base::Optional<int64_t> mm_id) {
+                                  std::optional<bool> curr,
+                                  std::optional<int64_t> mm_id) {
   const auto kRssStatUnknown = static_cast<uint32_t>(rss_members_.size()) - 1;
   if (member >= rss_members_.size()) {
     context_->storage->IncrementStats(stats::rss_stat_unknown_keys);
@@ -95,7 +95,7 @@
     return;
   }
 
-  base::Optional<UniqueTid> utid;
+  std::optional<UniqueTid> utid;
   if (mm_id.has_value() && curr.has_value()) {
     utid = FindUtidForMmId(*mm_id, *curr, pid);
   } else {
@@ -110,9 +110,9 @@
   }
 }
 
-base::Optional<UniqueTid> RssStatTracker::FindUtidForMmId(int64_t mm_id,
-                                                          bool is_curr,
-                                                          uint32_t pid) {
+std::optional<UniqueTid> RssStatTracker::FindUtidForMmId(int64_t mm_id,
+                                                         bool is_curr,
+                                                         uint32_t pid) {
   // If curr is true, we can just overwrite the state in the map and return
   // the utid correspodning to |pid|.
   if (is_curr) {
@@ -125,7 +125,7 @@
   // mm id.
   auto* it = mm_id_to_utid_.Find(mm_id);
   if (!it)
-    return base::nullopt;
+    return std::nullopt;
 
   // If the utid in the map is the same as our current utid but curr is false,
   // that means we are in the middle of a process changing mm structs (i.e. in
@@ -135,7 +135,7 @@
   const UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
   if (mm_utid == utid) {
     mm_id_to_utid_.Erase(mm_id);
-    return base::nullopt;
+    return std::nullopt;
   }
 
   // Verify that the utid in the map is still alive. This can happen if an mm
@@ -143,7 +143,7 @@
   // know the new process that struct will be associated with.
   if (!context_->process_tracker->IsThreadAlive(mm_utid)) {
     mm_id_to_utid_.Erase(mm_id);
-    return base::nullopt;
+    return std::nullopt;
   }
 
   // This case happens when a process is changing the VM of another process and
diff --git a/src/trace_processor/importers/ftrace/rss_stat_tracker.h b/src/trace_processor/importers/ftrace/rss_stat_tracker.h
index b3ba29a..7139d6c 100644
--- a/src/trace_processor/importers/ftrace/rss_stat_tracker.h
+++ b/src/trace_processor/importers/ftrace/rss_stat_tracker.h
@@ -40,13 +40,13 @@
                     uint32_t pid,
                     int64_t size,
                     uint32_t member,
-                    base::Optional<bool> curr,
-                    base::Optional<int64_t> mm_id);
+                    std::optional<bool> curr,
+                    std::optional<int64_t> mm_id);
 
  private:
-  base::Optional<UniqueTid> FindUtidForMmId(int64_t mm_id,
-                                            bool is_curr,
-                                            uint32_t pid);
+  std::optional<UniqueTid> FindUtidForMmId(int64_t mm_id,
+                                           bool is_curr,
+                                           uint32_t pid);
 
   base::FlatHashMap<int64_t, UniqueTid> mm_id_to_utid_;
   std::vector<StringId> rss_members_;
diff --git a/src/trace_processor/importers/ftrace/sched_event_tracker.cc b/src/trace_processor/importers/ftrace/sched_event_tracker.cc
index 3ee1fa1..f67e571 100644
--- a/src/trace_processor/importers/ftrace/sched_event_tracker.cc
+++ b/src/trace_processor/importers/ftrace/sched_event_tracker.cc
@@ -313,7 +313,7 @@
 StringId SchedEventTracker::TaskStateToStringId(int64_t task_state_int) {
   using ftrace_utils::TaskState;
 
-  base::Optional<VersionNumber> kernel_version =
+  std::optional<VersionNumber> kernel_version =
       SystemInfoTracker::GetOrCreate(context_)->GetKernelVersion();
   TaskState task_state = TaskState::FromRawPrevState(
       static_cast<uint16_t>(task_state_int), kernel_version);
diff --git a/src/trace_processor/importers/ftrace/sched_event_tracker_unittest.cc b/src/trace_processor/importers/ftrace/sched_event_tracker_unittest.cc
index 22b1139..5d275d6 100644
--- a/src/trace_processor/importers/ftrace/sched_event_tracker_unittest.cc
+++ b/src/trace_processor/importers/ftrace/sched_event_tracker_unittest.cc
@@ -68,7 +68,7 @@
 
   const auto& timestamps = context.storage->sched_slice_table().ts();
   ASSERT_EQ(timestamps[0], timestamp);
-  ASSERT_EQ(context.storage->thread_table().start_ts()[1], base::nullopt);
+  ASSERT_EQ(context.storage->thread_table().start_ts()[1], std::nullopt);
 
   auto name = context.storage->thread_table().name().GetString(1);
   ASSERT_STREQ(name.c_str(), kCommProc1);
@@ -102,7 +102,7 @@
 
   const auto& timestamps = context.storage->sched_slice_table().ts();
   ASSERT_EQ(timestamps[0], timestamp);
-  ASSERT_EQ(context.storage->thread_table().start_ts()[1], base::nullopt);
+  ASSERT_EQ(context.storage->thread_table().start_ts()[1], std::nullopt);
   ASSERT_EQ(context.storage->sched_slice_table().dur()[0], 1u);
   ASSERT_EQ(context.storage->sched_slice_table().dur()[1], 11u - 1u);
   ASSERT_EQ(context.storage->sched_slice_table().dur()[2], 31u - 11u);
@@ -125,14 +125,14 @@
                                  prio, prev_state,
                                  /*tid=*/1, kCommProc2, prio);
 
-  context.process_tracker->SetProcessMetadata(2, base::nullopt, "test",
+  context.process_tracker->SetProcessMetadata(2, std::nullopt, "test",
                                               base::StringView());
   context.process_tracker->UpdateThread(4, 2);
 
   ASSERT_EQ(context.storage->thread_table().tid()[1], 4u);
   ASSERT_EQ(context.storage->thread_table().upid()[1].value(), 1u);
   ASSERT_EQ(context.storage->process_table().pid()[1], 2u);
-  ASSERT_EQ(context.storage->process_table().start_ts()[1], base::nullopt);
+  ASSERT_EQ(context.storage->process_table().start_ts()[1], std::nullopt);
 }
 
 }  // namespace
diff --git a/src/trace_processor/importers/ftrace/thread_state_tracker.cc b/src/trace_processor/importers/ftrace/thread_state_tracker.cc
index 227a90a..3c6b123 100644
--- a/src/trace_processor/importers/ftrace/thread_state_tracker.cc
+++ b/src/trace_processor/importers/ftrace/thread_state_tracker.cc
@@ -67,19 +67,19 @@
 
   // Close the sleeping state and open runnable state.
   ClosePendingState(event_ts, utid, false);
-  AddOpenState(event_ts, utid, runnable_string_id_, base::nullopt, waker_utid);
+  AddOpenState(event_ts, utid, runnable_string_id_, std::nullopt, waker_utid);
 }
 
 void ThreadStateTracker::PushNewTaskEvent(int64_t event_ts,
                                          UniqueTid utid,
                                          UniqueTid waker_utid) {
-  AddOpenState(event_ts, utid, runnable_string_id_, base::nullopt, waker_utid);
+  AddOpenState(event_ts, utid, runnable_string_id_, std::nullopt, waker_utid);
 }
 
 void ThreadStateTracker::PushBlockedReason(
     UniqueTid utid,
-    base::Optional<bool> io_wait,
-    base::Optional<StringId> blocked_function) {
+    std::optional<bool> io_wait,
+    std::optional<StringId> blocked_function) {
   // Return if there is no state, as there is are no previous rows available.
   if (!HasPreviousRowNumbersForUtid(utid))
     return;
@@ -102,8 +102,8 @@
 void ThreadStateTracker::AddOpenState(int64_t ts,
                                       UniqueTid utid,
                                       StringId state,
-                                      base::Optional<uint32_t> cpu,
-                                      base::Optional<UniqueTid> waker_utid) {
+                                      std::optional<uint32_t> cpu,
+                                      std::optional<UniqueTid> waker_utid) {
   // Ignore utid 0 because it corresponds to the swapper thread which doesn't
   // make sense to insert.
   if (utid == 0)
@@ -124,11 +124,11 @@
   }
 
   if (!prev_row_numbers_for_thread_[utid].has_value()) {
-    prev_row_numbers_for_thread_[utid] = RelatedRows{base::nullopt, row_num};
+    prev_row_numbers_for_thread_[utid] = RelatedRows{std::nullopt, row_num};
   }
 
   if (IsRunning(state)) {
-    prev_row_numbers_for_thread_[utid] = RelatedRows{base::nullopt, row_num};
+    prev_row_numbers_for_thread_[utid] = RelatedRows{std::nullopt, row_num};
   } else if (IsBlocked(state)) {
     prev_row_numbers_for_thread_[utid] = RelatedRows{row_num, row_num};
   } else /* if (IsRunnable(state)) */ {
diff --git a/src/trace_processor/importers/ftrace/thread_state_tracker.h b/src/trace_processor/importers/ftrace/thread_state_tracker.h
index 3f8b7a5..7faae9c 100644
--- a/src/trace_processor/importers/ftrace/thread_state_tracker.h
+++ b/src/trace_processor/importers/ftrace/thread_state_tracker.h
@@ -57,15 +57,15 @@
 
   // Updates the current blocked state for utid with blocked reason.
   void PushBlockedReason(UniqueTid utid,
-                         base::Optional<bool> io_wait,
-                         base::Optional<StringId> blocked_function);
+                         std::optional<bool> io_wait,
+                         std::optional<StringId> blocked_function);
 
  private:
   void AddOpenState(int64_t ts,
                     UniqueTid utid,
                     StringId state,
-                    base::Optional<uint32_t> cpu = base::nullopt,
-                    base::Optional<UniqueTid> waker_utid = base::nullopt);
+                    std::optional<uint32_t> cpu = std::nullopt,
+                    std::optional<UniqueTid> waker_utid = std::nullopt);
   void ClosePendingState(int64_t end_ts, UniqueTid utid, bool data_loss);
 
   bool IsRunning(StringId state);
@@ -89,11 +89,11 @@
   StringId runnable_string_id_;
 
   struct RelatedRows {
-    base::Optional<tables::ThreadStateTable::RowNumber> last_blocked_row;
+    std::optional<tables::ThreadStateTable::RowNumber> last_blocked_row;
     tables::ThreadStateTable::RowNumber last_row;
   };
 
-  std::vector<base::Optional<RelatedRows>> prev_row_numbers_for_thread_;
+  std::vector<std::optional<RelatedRows>> prev_row_numbers_for_thread_;
 };
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/ftrace/thread_state_tracker_unittest.cc b/src/trace_processor/importers/ftrace/thread_state_tracker_unittest.cc
index 0c6c370..e706a2e 100644
--- a/src/trace_processor/importers/ftrace/thread_state_tracker_unittest.cc
+++ b/src/trace_processor/importers/ftrace/thread_state_tracker_unittest.cc
@@ -58,13 +58,13 @@
   void VerifyThreadState(
       const tables::ThreadStateTable::ConstIterator& it,
       int64_t from,
-      base::Optional<int64_t> to,
+      std::optional<int64_t> to,
       UniqueTid utid,
       const char* state,
-      base::Optional<bool> io_wait = base::nullopt,
-      base::Optional<StringId> blocked_function = base::nullopt,
-      base::Optional<UniqueTid> waker_utid = base::nullopt,
-      base::Optional<int64_t> cpu = base::nullopt) {
+      std::optional<bool> io_wait = std::nullopt,
+      std::optional<StringId> blocked_function = std::nullopt,
+      std::optional<UniqueTid> waker_utid = std::nullopt,
+      std::optional<int64_t> cpu = std::nullopt) {
     ASSERT_EQ(it.ts(), from);
     ASSERT_EQ(it.dur(), to ? *to - from : -1);
     ASSERT_EQ(it.utid(), utid);
@@ -75,7 +75,7 @@
         ASSERT_EQ(it.cpu(), CPU_A);
       }
     } else {
-      ASSERT_EQ(it.cpu(), base::nullopt);
+      ASSERT_EQ(it.cpu(), std::nullopt);
     }
     ASSERT_STREQ(context_.storage->GetString(it.state()).c_str(), state);
     ASSERT_EQ(it.io_wait(), io_wait);
@@ -97,8 +97,8 @@
 
   ASSERT_EQ(context_.storage->thread_state_table().row_count(), 2ul);
   auto rows_it = ThreadStateIterator();
-  VerifyThreadState(rows_it, 10, base::nullopt, THREAD_A, "S");
-  VerifyThreadState(++rows_it, 10, base::nullopt, THREAD_B, kRunning);
+  VerifyThreadState(rows_it, 10, std::nullopt, THREAD_A, "S");
+  VerifyThreadState(++rows_it, 10, std::nullopt, THREAD_B, kRunning);
 }
 
 TEST_F(ThreadStateTrackerUnittest, StartWithWakingEvent) {
@@ -114,9 +114,9 @@
   ASSERT_EQ(context_.storage->thread_state_table().row_count(), 3ul);
   auto row_it = ThreadStateIterator();
   VerifyThreadState(row_it, 10, 20, THREAD_A, "S");
-  VerifyThreadState(++row_it, 10, base::nullopt, THREAD_B, kRunning);
-  VerifyThreadState(++row_it, 20, base::nullopt, THREAD_A, kRunnable,
-                    base::nullopt, base::nullopt, THREAD_C);
+  VerifyThreadState(++row_it, 10, std::nullopt, THREAD_B, kRunning);
+  VerifyThreadState(++row_it, 20, std::nullopt, THREAD_A, kRunnable,
+                    std::nullopt, std::nullopt, THREAD_C);
 }
 
 TEST_F(ThreadStateTrackerUnittest, BasicPushBlockedReason) {
@@ -125,7 +125,7 @@
   tracker_->PushBlockedReason(THREAD_A, true, StringIdOf(kBlockedFunction));
 
   auto rows_it = ThreadStateIterator();
-  VerifyThreadState(rows_it, 10, base::nullopt, THREAD_A, "S", true,
+  VerifyThreadState(rows_it, 10, std::nullopt, THREAD_A, "S", true,
                     StringIdOf(kBlockedFunction));
 }
 
@@ -150,7 +150,7 @@
 
   // The opening of idle_thred should be discarded so the first row will be
   // for the THREAD_A.
-  VerifyThreadState(rows_it, 10, base::nullopt, THREAD_A, kRunning);
+  VerifyThreadState(rows_it, 10, std::nullopt, THREAD_A, kRunning);
 }
 
 TEST_F(ThreadStateTrackerUnittest, SchedBlockedReasonWithIdleThread) {
@@ -158,19 +158,19 @@
                                  THREAD_A);
   tracker_->PushSchedSwitchEvent(2, CPU_A, THREAD_A, StringIdOf("D"),
                                  IDLE_THREAD);
-  tracker_->PushBlockedReason(THREAD_A, IDLE_THREAD, base::nullopt);
+  tracker_->PushBlockedReason(THREAD_A, IDLE_THREAD, std::nullopt);
   tracker_->PushSchedSwitchEvent(3, CPU_A, IDLE_THREAD, StringIdOf("D"),
                                  THREAD_B);
   tracker_->PushSchedSwitchEvent(4, CPU_A, THREAD_B, StringIdOf("D"),
                                  IDLE_THREAD);
-  tracker_->PushBlockedReason(THREAD_B, 1, base::nullopt);
+  tracker_->PushBlockedReason(THREAD_B, 1, std::nullopt);
 
   auto rows_it = ThreadStateIterator();
 
   VerifyThreadState(rows_it, 1, 2, THREAD_A, kRunning);
-  VerifyThreadState(++rows_it, 2, base::nullopt, THREAD_A, "D", 0);
+  VerifyThreadState(++rows_it, 2, std::nullopt, THREAD_A, "D", 0);
   VerifyThreadState(++rows_it, 3, 4, THREAD_B, kRunning);
-  VerifyThreadState(++rows_it, 4, base::nullopt, THREAD_B, "D", 1);
+  VerifyThreadState(++rows_it, 4, std::nullopt, THREAD_B, "D", 1);
 }
 
 TEST_F(ThreadStateTrackerUnittest, SchedSwitchForcedMigration) {
@@ -178,7 +178,7 @@
   tracker_->PushSchedSwitchEvent(2, CPU_A, THREAD_A, StringIdOf("S"), THREAD_B);
 
   auto rows_it = ThreadStateIterator();
-  VerifyThreadState(rows_it, 1, base::nullopt, THREAD_A, "S");
+  VerifyThreadState(rows_it, 1, std::nullopt, THREAD_A, "S");
   VerifyThreadState(++rows_it, 1, 2, THREAD_B, kRunning);
 }
 
@@ -192,12 +192,12 @@
   tracker_->PushSchedSwitchEvent(7, CPU_A, 0, StringIdOf(kRunnable), 18);
 
   auto rows_it = ThreadStateIterator();
-  VerifyThreadState(rows_it, 2, base::nullopt, 11, "S");
-  VerifyThreadState(++rows_it, 3, base::nullopt, 8, "S");
-  VerifyThreadState(++rows_it, 4, base::nullopt, 17771, "S");
+  VerifyThreadState(rows_it, 2, std::nullopt, 11, "S");
+  VerifyThreadState(++rows_it, 3, std::nullopt, 8, "S");
+  VerifyThreadState(++rows_it, 4, std::nullopt, 17771, "S");
   VerifyThreadState(++rows_it, 4, 5, 17772, kRunning);
-  VerifyThreadState(++rows_it, 5, base::nullopt, 17772, "S");
-  VerifyThreadState(++rows_it, 7, base::nullopt, 18, kRunning);
+  VerifyThreadState(++rows_it, 5, std::nullopt, 17772, "S");
+  VerifyThreadState(++rows_it, 7, std::nullopt, 18, kRunning);
 }
 
 TEST_F(ThreadStateTrackerUnittest, RunningOnMultipleCPUsForcedMigration) {
@@ -206,11 +206,11 @@
   tracker_->PushSchedSwitchEvent(2, CPU_B, THREAD_B, StringIdOf("S"), THREAD_A);
 
   auto rows_it = ThreadStateIterator();
-  VerifyThreadState(rows_it, 1, base::nullopt, THREAD_C, "S");
+  VerifyThreadState(rows_it, 1, std::nullopt, THREAD_C, "S");
   VerifyThreadState(++rows_it, 1, 2, THREAD_A, kRunning);
-  VerifyThreadState(++rows_it, 2, base::nullopt, THREAD_B, "S");
-  VerifyThreadState(++rows_it, 2, base::nullopt, THREAD_A, kRunning,
-                    base::nullopt, base::nullopt, base::nullopt, CPU_B);
+  VerifyThreadState(++rows_it, 2, std::nullopt, THREAD_B, "S");
+  VerifyThreadState(++rows_it, 2, std::nullopt, THREAD_A, kRunning,
+                    std::nullopt, std::nullopt, std::nullopt, CPU_B);
 }
 
 }  // namespace
diff --git a/src/trace_processor/importers/ftrace/v4l2_tracker.cc b/src/trace_processor/importers/ftrace/v4l2_tracker.cc
index 18546e9..c441fb1 100644
--- a/src/trace_processor/importers/ftrace/v4l2_tracker.cc
+++ b/src/trace_processor/importers/ftrace/v4l2_tracker.cc
@@ -88,7 +88,7 @@
 
       StringId buf_name_id =
           context_->storage->InternString(buf_name.string_view());
-      base::Optional<SliceId> slice_id =
+      std::optional<SliceId> slice_id =
           AddSlice(buf_name_id, timestamp, pid, evt);
 
       uint64_t hash = base::Hasher::Combine(evt.device_minor, evt.sequence,
@@ -129,7 +129,7 @@
 
       StringId buf_name_id =
           context_->storage->InternString(buf_name.string_view());
-      base::Optional<SliceId> slice_id =
+      std::optional<SliceId> slice_id =
           AddSlice(buf_name_id, timestamp, pid, evt);
 
       uint64_t hash = base::Hasher::Combine(evt.device_minor, evt.sequence,
@@ -150,9 +150,9 @@
       Vb2V4l2BufQueueFtraceEvent::Decoder pb_evt(bytes.data, bytes.size);
       BufferEvent evt;
       evt.device_minor = pb_evt.minor();
-      evt.index = base::nullopt;
-      evt.type = base::nullopt;
-      evt.bytesused = base::nullopt;
+      evt.index = std::nullopt;
+      evt.type = std::nullopt;
+      evt.bytesused = std::nullopt;
       evt.flags = pb_evt.flags();
       evt.field = pb_evt.field();
       evt.timestamp = pb_evt.timestamp();
@@ -181,9 +181,9 @@
       Vb2V4l2BufDoneFtraceEvent::Decoder pb_evt(bytes.data, bytes.size);
       BufferEvent evt;
       evt.device_minor = pb_evt.minor();
-      evt.index = base::nullopt;
-      evt.type = base::nullopt;
-      evt.bytesused = base::nullopt;
+      evt.index = std::nullopt;
+      evt.type = std::nullopt;
+      evt.bytesused = std::nullopt;
       evt.flags = pb_evt.flags();
       evt.field = pb_evt.field();
       evt.timestamp = pb_evt.timestamp();
@@ -212,9 +212,9 @@
       Vb2V4l2QbufFtraceEvent::Decoder pb_evt(bytes.data, bytes.size);
       BufferEvent evt;
       evt.device_minor = pb_evt.minor();
-      evt.index = base::nullopt;
-      evt.type = base::nullopt;
-      evt.bytesused = base::nullopt;
+      evt.index = std::nullopt;
+      evt.type = std::nullopt;
+      evt.bytesused = std::nullopt;
       evt.flags = pb_evt.flags();
       evt.field = pb_evt.field();
       evt.timestamp = pb_evt.timestamp();
@@ -243,9 +243,9 @@
       Vb2V4l2DqbufFtraceEvent::Decoder pb_evt(bytes.data, bytes.size);
       BufferEvent evt;
       evt.device_minor = pb_evt.minor();
-      evt.index = base::nullopt;
-      evt.type = base::nullopt;
-      evt.bytesused = base::nullopt;
+      evt.index = std::nullopt;
+      evt.type = std::nullopt;
+      evt.bytesused = std::nullopt;
       evt.flags = pb_evt.flags();
       evt.field = pb_evt.field();
       evt.timestamp = pb_evt.timestamp();
@@ -275,14 +275,14 @@
   }
 }
 
-base::Optional<SliceId> V4l2Tracker::AddSlice(StringId buf_name_id,
-                                              int64_t timestamp,
-                                              uint32_t pid,
-                                              const BufferEvent& evt) {
+std::optional<SliceId> V4l2Tracker::AddSlice(StringId buf_name_id,
+                                             int64_t timestamp,
+                                             uint32_t pid,
+                                             const BufferEvent& evt) {
   UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
   TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
 
-  base::Optional<SliceId> slice_id = context_->slice_tracker->Scoped(
+  std::optional<SliceId> slice_id = context_->slice_tracker->Scoped(
       timestamp, track_id, buf_event_ids_.v4l2, buf_name_id, 0,
       [this, &evt](ArgsTracker::BoundInserter* inserter) {
         this->AddArgs(evt, inserter);
diff --git a/src/trace_processor/importers/ftrace/v4l2_tracker.h b/src/trace_processor/importers/ftrace/v4l2_tracker.h
index f204e1d..057d7e4 100644
--- a/src/trace_processor/importers/ftrace/v4l2_tracker.h
+++ b/src/trace_processor/importers/ftrace/v4l2_tracker.h
@@ -19,9 +19,9 @@
 
 #include <stdint.h>
 #include <cstdint>
+#include <optional>
 
 #include "perfetto/ext/base/flat_hash_map.h"
-#include "perfetto/ext/base/optional.h"
 
 #include "perfetto/protozero/field.h"
 #include "src/trace_processor/importers/common/args_tracker.h"
@@ -58,9 +58,9 @@
   struct BufferEvent {
    public:
     int32_t device_minor;
-    base::Optional<uint32_t> index;
-    base::Optional<uint32_t> type;
-    base::Optional<uint32_t> bytesused;
+    std::optional<uint32_t> index;
+    std::optional<uint32_t> type;
+    std::optional<uint32_t> bytesused;
     uint32_t flags;
     uint32_t field;
     int64_t timestamp;
@@ -155,13 +155,13 @@
   };
 
   struct QueuedBuffer {
-    base::Optional<SliceId> queue_slice_id;
+    std::optional<SliceId> queue_slice_id;
   };
 
-  base::Optional<SliceId> AddSlice(StringId buf_name_id,
-                                   int64_t timestamp,
-                                   uint32_t pid,
-                                   const BufferEvent& evt);
+  std::optional<SliceId> AddSlice(StringId buf_name_id,
+                                  int64_t timestamp,
+                                  uint32_t pid,
+                                  const BufferEvent& evt);
 
   void AddArgs(const BufferEvent& evt, ArgsTracker::BoundInserter* inserter);
 
diff --git a/src/trace_processor/importers/fuchsia/fuchsia_parser_unittest.cc b/src/trace_processor/importers/fuchsia/fuchsia_parser_unittest.cc
index 349e9d0..cf0d569 100644
--- a/src/trace_processor/importers/fuchsia/fuchsia_parser_unittest.cc
+++ b/src/trace_processor/importers/fuchsia/fuchsia_parser_unittest.cc
@@ -115,7 +115,7 @@
 
   MOCK_METHOD4(SetProcessMetadata,
                UniquePid(uint32_t pid,
-                         base::Optional<uint32_t> ppid,
+                         std::optional<uint32_t> ppid,
                          base::StringView process_name,
                          base::StringView cmdline));
 
@@ -169,9 +169,9 @@
                     int32_t next_prio));
 
   MOCK_METHOD3(PushCounter,
-               base::Optional<CounterId>(int64_t timestamp,
-                                         double value,
-                                         TrackId track_id));
+               std::optional<CounterId>(int64_t timestamp,
+                                        double value,
+                                        TrackId track_id));
 };
 
 class MockSliceTracker : public SliceTracker {
@@ -180,29 +180,29 @@
       : SliceTracker(context) {}
 
   MOCK_METHOD5(Begin,
-               base::Optional<SliceId>(int64_t timestamp,
-                                       TrackId track_id,
-                                       StringId cat,
-                                       StringId name,
-                                       SetArgsCallback args_callback));
+               std::optional<SliceId>(int64_t timestamp,
+                                      TrackId track_id,
+                                      StringId cat,
+                                      StringId name,
+                                      SetArgsCallback args_callback));
   MOCK_METHOD5(End,
-               base::Optional<SliceId>(int64_t timestamp,
-                                       TrackId track_id,
-                                       StringId cat,
-                                       StringId name,
-                                       SetArgsCallback args_callback));
+               std::optional<SliceId>(int64_t timestamp,
+                                      TrackId track_id,
+                                      StringId cat,
+                                      StringId name,
+                                      SetArgsCallback args_callback));
   MOCK_METHOD6(Scoped,
-               base::Optional<SliceId>(int64_t timestamp,
-                                       TrackId track_id,
-                                       StringId cat,
-                                       StringId name,
-                                       int64_t duration,
-                                       SetArgsCallback args_callback));
+               std::optional<SliceId>(int64_t timestamp,
+                                      TrackId track_id,
+                                      StringId cat,
+                                      StringId name,
+                                      int64_t duration,
+                                      SetArgsCallback args_callback));
   MOCK_METHOD4(StartSlice,
-               base::Optional<SliceId>(int64_t timestamp,
-                                       TrackId track_id,
-                                       SetArgsCallback args_callback,
-                                       std::function<SliceId()> inserter));
+               std::optional<SliceId>(int64_t timestamp,
+                                      TrackId track_id,
+                                      SetArgsCallback args_callback,
+                                      std::function<SliceId()> inserter));
 };
 
 class FuchsiaTraceParserTest : public ::testing::Test {
diff --git a/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.cc b/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.cc
index 514bfcb..d2dbb1f 100644
--- a/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.cc
+++ b/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.cc
@@ -593,7 +593,7 @@
           // artificial koids which have the 2^63 bit set. This is used for
           // things such as virtual threads.
           procs->SetProcessMetadata(
-              static_cast<uint32_t>(obj_id), base::Optional<uint32_t>(),
+              static_cast<uint32_t>(obj_id), std::optional<uint32_t>(),
               base::StringView(storage->GetString(name)), base::StringView());
           break;
         }
diff --git a/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.h b/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.h
index 4b4d610..671060e 100644
--- a/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.h
+++ b/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.h
@@ -73,8 +73,8 @@
 
     FuchsiaThreadInfo info;
     int64_t last_ts{0};
-    base::Optional<tables::SchedSliceTable::RowNumber> last_slice_row;
-    base::Optional<tables::ThreadStateTable::RowNumber> last_state_row;
+    std::optional<tables::SchedSliceTable::RowNumber> last_slice_row;
+    std::optional<tables::ThreadStateTable::RowNumber> last_state_row;
   };
 
   void SwitchFrom(Thread* thread,
diff --git a/src/trace_processor/importers/json/json_trace_parser.cc b/src/trace_processor/importers/json/json_trace_parser.cc
index 50cf243..b087963 100644
--- a/src/trace_processor/importers/json/json_trace_parser.cc
+++ b/src/trace_processor/importers/json/json_trace_parser.cc
@@ -18,10 +18,10 @@
 
 #include <cinttypes>
 #include <limits>
+#include <optional>
 #include <string>
 
 #include "perfetto/base/logging.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/string_view.h"
 #include "src/trace_processor/importers/common/event_tracker.h"
@@ -39,16 +39,16 @@
 #if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
 namespace {
 
-base::Optional<uint64_t> MaybeExtractFlowIdentifier(const Json::Value& value,
-                                                    bool version2) {
+std::optional<uint64_t> MaybeExtractFlowIdentifier(const Json::Value& value,
+                                                   bool version2) {
   std::string id_key = (version2 ? "bind_id" : "id");
   if (!value.isMember(id_key))
-    return base::nullopt;
+    return std::nullopt;
   auto id = value[id_key];
   if (id.isNumeric())
     return id.asUInt64();
   if (!id.isString())
-    return base::nullopt;
+    return std::nullopt;
   const char* c_string = id.asCString();
   return base::CStringToUInt64(c_string, 16);
 }
@@ -87,8 +87,8 @@
     return;
   char phase = *ph.asCString();
 
-  base::Optional<uint32_t> opt_pid;
-  base::Optional<uint32_t> opt_tid;
+  std::optional<uint32_t> opt_pid;
+  std::optional<uint32_t> opt_tid;
 
   if (value.isMember("pid"))
     opt_pid = json::CoerceToUint32(value["pid"]);
@@ -132,8 +132,8 @@
     // tdur will only exist on 'X' events.
     row.thread_dur = json::CoerceToTs(value["tdur"]);
     // JSON traces don't report these counters as part of slices.
-    row.thread_instruction_count = base::nullopt;
-    row.thread_instruction_delta = base::nullopt;
+    row.thread_instruction_count = std::nullopt;
+    row.thread_instruction_delta = std::nullopt;
     return row;
   };
 
@@ -191,7 +191,7 @@
       break;
     }
     case 'X': {  // TRACE_EVENT (scoped event).
-      base::Optional<int64_t> opt_dur = json::CoerceToTs(value["dur"]);
+      std::optional<int64_t> opt_dur = json::CoerceToTs(value["dur"]);
       if (!opt_dur.has_value())
         return;
       TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
@@ -333,7 +333,7 @@
       }
       if (name == "process_name" && !value["args"]["name"].empty()) {
         const char* proc_name = value["args"]["name"].asCString();
-        procs->SetProcessMetadata(pid, base::nullopt, proc_name,
+        procs->SetProcessMetadata(pid, std::nullopt, proc_name,
                                   base::StringView());
         break;
       }
diff --git a/src/trace_processor/importers/json/json_trace_tokenizer.cc b/src/trace_processor/importers/json/json_trace_tokenizer.cc
index a385edf..12414f2 100644
--- a/src/trace_processor/importers/json/json_trace_tokenizer.cc
+++ b/src/trace_processor/importers/json/json_trace_tokenizer.cc
@@ -298,7 +298,7 @@
 
 base::Status ExtractValueForJsonKey(base::StringView dict,
                                     const std::string& key,
-                                    base::Optional<std::string>* value) {
+                                    std::optional<std::string>* value) {
   PERFETTO_DCHECK(dict.size() >= 2);
 
   const char* start = dict.data();
@@ -398,7 +398,7 @@
   if (state != kAfterDict)
     return base::ErrStatus("Failure parsing JSON: malformed dictionary");
 
-  *value = base::nullopt;
+  *value = std::nullopt;
   return base::OkStatus();
 }
 
@@ -531,16 +531,16 @@
         break;
     }
 
-    base::Optional<std::string> opt_raw_ts;
+    std::optional<std::string> opt_raw_ts;
     RETURN_IF_ERROR(ExtractValueForJsonKey(unparsed, "ts", &opt_raw_ts));
-    base::Optional<int64_t> opt_ts =
-        opt_raw_ts ? json::CoerceToTs(*opt_raw_ts) : base::nullopt;
+    std::optional<int64_t> opt_ts =
+        opt_raw_ts ? json::CoerceToTs(*opt_raw_ts) : std::nullopt;
     int64_t ts = 0;
     if (opt_ts.has_value()) {
       ts = opt_ts.value();
     } else {
       // Metadata events may omit ts. In all other cases error:
-      base::Optional<std::string> opt_raw_ph;
+      std::optional<std::string> opt_raw_ph;
       RETURN_IF_ERROR(ExtractValueForJsonKey(unparsed, "ph", &opt_raw_ph));
       if (!opt_raw_ph || *opt_raw_ph != "M") {
         context_->storage->IncrementStats(stats::json_tokenizer_failure);
diff --git a/src/trace_processor/importers/json/json_trace_tokenizer.h b/src/trace_processor/importers/json/json_trace_tokenizer.h
index 3713636..61f1d77 100644
--- a/src/trace_processor/importers/json/json_trace_tokenizer.h
+++ b/src/trace_processor/importers/json/json_trace_tokenizer.h
@@ -79,7 +79,7 @@
 // Visible for testing.
 base::Status ExtractValueForJsonKey(base::StringView dict,
                                     const std::string& key,
-                                    base::Optional<std::string>* value);
+                                    std::optional<std::string>* value);
 
 enum class ReadSystemLineRes {
   kFoundLine,
diff --git a/src/trace_processor/importers/json/json_trace_tokenizer_unittest.cc b/src/trace_processor/importers/json/json_trace_tokenizer_unittest.cc
index 051f52a..22a3885 100644
--- a/src/trace_processor/importers/json/json_trace_tokenizer_unittest.cc
+++ b/src/trace_processor/importers/json/json_trace_tokenizer_unittest.cc
@@ -226,7 +226,7 @@
 }
 
 TEST(JsonTraceTokenizerTest, ExtractValueForJsonKey) {
-  base::Optional<std::string> line;
+  std::optional<std::string> line;
 
   ASSERT_TRUE(ExtractValueForJsonKey(R"({"ts": 149029})", "ts", &line).ok());
   ASSERT_EQ(*line, "149029");
diff --git a/src/trace_processor/importers/json/json_utils.cc b/src/trace_processor/importers/json/json_utils.cc
index 432ddbe..be9492b 100644
--- a/src/trace_processor/importers/json/json_utils.cc
+++ b/src/trace_processor/importers/json/json_utils.cc
@@ -37,7 +37,7 @@
 #endif
 }
 
-base::Optional<int64_t> CoerceToTs(const Json::Value& value) {
+std::optional<int64_t> CoerceToTs(const Json::Value& value) {
   PERFETTO_DCHECK(IsJsonSupported());
 
 #if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
@@ -50,36 +50,36 @@
     case Json::stringValue:
       return CoerceToTs(value.asString());
     default:
-      return base::nullopt;
+      return std::nullopt;
   }
 #else
   perfetto::base::ignore_result(value);
-  return base::nullopt;
+  return std::nullopt;
 #endif
 }
 
-base::Optional<int64_t> CoerceToTs(const std::string& s) {
+std::optional<int64_t> CoerceToTs(const std::string& s) {
   PERFETTO_DCHECK(IsJsonSupported());
 
 #if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
   size_t lhs_end = std::min<size_t>(s.find('.'), s.size());
   size_t rhs_start = std::min<size_t>(lhs_end + 1, s.size());
-  base::Optional<int64_t> lhs = base::StringToInt64(s.substr(0, lhs_end));
-  base::Optional<double> rhs =
+  std::optional<int64_t> lhs = base::StringToInt64(s.substr(0, lhs_end));
+  std::optional<double> rhs =
       base::StringToDouble("0." + s.substr(rhs_start, std::string::npos));
   if ((!lhs.has_value() && lhs_end > 0) ||
       (!rhs.has_value() && rhs_start < s.size())) {
-    return base::nullopt;
+    return std::nullopt;
   }
   return lhs.value_or(0) * 1000 +
          static_cast<int64_t>(rhs.value_or(0) * 1000.0);
 #else
   perfetto::base::ignore_result(s);
-  return base::nullopt;
+  return std::nullopt;
 #endif
 }
 
-base::Optional<int64_t> CoerceToInt64(const Json::Value& value) {
+std::optional<int64_t> CoerceToInt64(const Json::Value& value) {
   PERFETTO_DCHECK(IsJsonSupported());
 
 #if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
@@ -94,36 +94,36 @@
       char* end;
       int64_t n = strtoll(s.c_str(), &end, 10);
       if (end != s.data() + s.size())
-        return base::nullopt;
+        return std::nullopt;
       return n;
     }
     default:
-      return base::nullopt;
+      return std::nullopt;
   }
 #else
   perfetto::base::ignore_result(value);
-  return base::nullopt;
+  return std::nullopt;
 #endif
 }
 
-base::Optional<uint32_t> CoerceToUint32(const Json::Value& value) {
+std::optional<uint32_t> CoerceToUint32(const Json::Value& value) {
   PERFETTO_DCHECK(IsJsonSupported());
 
 #if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
-  base::Optional<int64_t> result = CoerceToInt64(value);
+  std::optional<int64_t> result = CoerceToInt64(value);
   if (!result.has_value())
-    return base::nullopt;
+    return std::nullopt;
   int64_t n = result.value();
   if (n < 0 || n > std::numeric_limits<uint32_t>::max())
-    return base::nullopt;
+    return std::nullopt;
   return static_cast<uint32_t>(n);
 #else
   perfetto::base::ignore_result(value);
-  return base::nullopt;
+  return std::nullopt;
 #endif
 }
 
-base::Optional<Json::Value> ParseJsonString(base::StringView raw_string) {
+std::optional<Json::Value> ParseJsonString(base::StringView raw_string) {
   PERFETTO_DCHECK(IsJsonSupported());
 
 #if PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
@@ -133,11 +133,11 @@
   Json::Value value;
   const char* begin = raw_string.data();
   return reader->parse(begin, begin + raw_string.size(), &value, nullptr)
-             ? base::make_optional(std::move(value))
-             : base::nullopt;
+             ? std::make_optional(std::move(value))
+             : std::nullopt;
 #else
   perfetto::base::ignore_result(raw_string);
-  return base::nullopt;
+  return std::nullopt;
 #endif
 }
 
diff --git a/src/trace_processor/importers/json/json_utils.h b/src/trace_processor/importers/json/json_utils.h
index 1cbcae8..b9d0762 100644
--- a/src/trace_processor/importers/json/json_utils.h
+++ b/src/trace_processor/importers/json/json_utils.h
@@ -18,8 +18,8 @@
 #define SRC_TRACE_PROCESSOR_IMPORTERS_JSON_JSON_UTILS_H_
 
 #include <stdint.h>
+#include <optional>
 
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_view.h"
 
 #include "src/trace_processor/importers/common/args_tracker.h"
@@ -40,14 +40,14 @@
 // build flags.
 bool IsJsonSupported();
 
-base::Optional<int64_t> CoerceToTs(const Json::Value& value);
-base::Optional<int64_t> CoerceToTs(const std::string& value);
-base::Optional<int64_t> CoerceToInt64(const Json::Value& value);
-base::Optional<uint32_t> CoerceToUint32(const Json::Value& value);
+std::optional<int64_t> CoerceToTs(const Json::Value& value);
+std::optional<int64_t> CoerceToTs(const std::string& value);
+std::optional<int64_t> CoerceToInt64(const Json::Value& value);
+std::optional<uint32_t> CoerceToUint32(const Json::Value& value);
 
 // Parses the given JSON string into a JSON::Value object.
 // This function should only be called if |IsJsonSupported()| returns true.
-base::Optional<Json::Value> ParseJsonString(base::StringView raw_string);
+std::optional<Json::Value> ParseJsonString(base::StringView raw_string);
 
 // Flattens the given Json::Value and adds each leaf node to the bound args
 // inserter. Note:
diff --git a/src/trace_processor/importers/memory_tracker/graph_processor.cc b/src/trace_processor/importers/memory_tracker/graph_processor.cc
index 85dc9ad..77e40a9 100644
--- a/src/trace_processor/importers/memory_tracker/graph_processor.cc
+++ b/src/trace_processor/importers/memory_tracker/graph_processor.cc
@@ -42,14 +42,14 @@
   }
 }
 
-base::Optional<uint64_t> GetSizeEntryOfNode(Node* node) {
+std::optional<uint64_t> GetSizeEntryOfNode(Node* node) {
   auto size_it = node->entries()->find(kSizeEntryName);
   if (size_it == node->entries()->end())
-    return base::nullopt;
+    return std::nullopt;
 
   PERFETTO_DCHECK(size_it->second.type == Node::Entry::Type::kUInt64);
   PERFETTO_DCHECK(size_it->second.units == Node::Entry::ScalarUnits::kBytes);
-  return base::Optional<uint64_t>(size_it->second.value_uint64);
+  return std::optional<uint64_t>(size_it->second.value_uint64);
 }
 
 }  // namespace
@@ -530,17 +530,17 @@
 }
 
 // static
-base::Optional<uint64_t> GraphProcessor::AggregateSizeForDescendantNode(
+std::optional<uint64_t> GraphProcessor::AggregateSizeForDescendantNode(
     Node* root,
     Node* descendant) {
   Edge* owns_edge = descendant->owns_edge();
   if (owns_edge && owns_edge->target()->IsDescendentOf(*root))
-    return base::make_optional(0UL);
+    return std::make_optional(0UL);
 
   if (descendant->children()->empty())
     return GetSizeEntryOfNode(descendant).value_or(0ul);
 
-  base::Optional<uint64_t> size;
+  std::optional<uint64_t> size;
   for (const auto& path_to_child : *descendant->children()) {
     auto c_size = AggregateSizeForDescendantNode(root, path_to_child.second);
     if (size) {
@@ -556,10 +556,10 @@
 // static
 void GraphProcessor::CalculateSizeForNode(Node* node) {
   // Get the size at the root node if it exists.
-  base::Optional<uint64_t> node_size = GetSizeEntryOfNode(node);
+  std::optional<uint64_t> node_size = GetSizeEntryOfNode(node);
 
   // Aggregate the size of all the child nodes.
-  base::Optional<uint64_t> aggregated_size;
+  std::optional<uint64_t> aggregated_size;
   for (const auto& path_to_child : *node->children()) {
     auto c_size = AggregateSizeForDescendantNode(node, path_to_child.second);
     if (aggregated_size) {
@@ -578,7 +578,7 @@
   // *aggregated_size);
 
   // Calculate the maximal size of an owner node.
-  base::Optional<uint64_t> max_owner_size;
+  std::optional<uint64_t> max_owner_size;
   for (auto* edge : *node->owned_by_edges()) {
     auto o_size = GetSizeEntryOfNode(edge->source());
     if (max_owner_size) {
@@ -625,7 +625,7 @@
 // static
 void GraphProcessor::CalculateNodeSubSizes(Node* node) {
   // Completely skip nodes with undefined size.
-  base::Optional<uint64_t> size_opt = GetSizeEntryOfNode(node);
+  std::optional<uint64_t> size_opt = GetSizeEntryOfNode(node);
   if (!size_opt)
     return;
 
@@ -669,7 +669,7 @@
 // static
 void GraphProcessor::CalculateNodeOwnershipCoefficient(Node* node) {
   // Completely skip nodes with undefined size.
-  base::Optional<uint64_t> size_opt = GetSizeEntryOfNode(node);
+  std::optional<uint64_t> size_opt = GetSizeEntryOfNode(node);
   if (!size_opt)
     return;
 
@@ -744,7 +744,7 @@
 // static
 void GraphProcessor::CalculateNodeCumulativeOwnershipCoefficient(Node* node) {
   // Completely skip nodes with undefined size.
-  base::Optional<uint64_t> size_opt = GetSizeEntryOfNode(node);
+  std::optional<uint64_t> size_opt = GetSizeEntryOfNode(node);
   if (!size_opt)
     return;
 
@@ -771,7 +771,7 @@
 void GraphProcessor::CalculateNodeEffectiveSize(Node* node) {
   // Completely skip nodes with undefined size. As a result, each node will
   // have defined effective size if and only if it has defined size.
-  base::Optional<uint64_t> size_opt = GetSizeEntryOfNode(node);
+  std::optional<uint64_t> size_opt = GetSizeEntryOfNode(node);
   if (!size_opt) {
     node->entries()->erase(kEffectiveSizeEntryName);
     return;
diff --git a/src/trace_processor/importers/memory_tracker/graph_processor_unittest.cc b/src/trace_processor/importers/memory_tracker/graph_processor_unittest.cc
index 4275b36..2a96ee7 100644
--- a/src/trace_processor/importers/memory_tracker/graph_processor_unittest.cc
+++ b/src/trace_processor/importers/memory_tracker/graph_processor_unittest.cc
@@ -71,8 +71,8 @@
     GraphProcessor::PropagateNumericsAndDiagnosticsRecursively(node);
   }
 
-  base::Optional<uint64_t> AggregateSizeForDescendantNode(Node* root,
-                                                          Node* descendant) {
+  std::optional<uint64_t> AggregateSizeForDescendantNode(Node* root,
+                                                         Node* descendant) {
     return GraphProcessor::AggregateSizeForDescendantNode(root, descendant);
   }
 
diff --git a/src/trace_processor/importers/proto/active_chrome_processes_tracker.cc b/src/trace_processor/importers/proto/active_chrome_processes_tracker.cc
index 1c20836..a689cec 100644
--- a/src/trace_processor/importers/proto/active_chrome_processes_tracker.cc
+++ b/src/trace_processor/importers/proto/active_chrome_processes_tracker.cc
@@ -31,8 +31,8 @@
   for (auto it = process_data_.GetIterator(); it; ++it) {
     UniquePid upid = it.key();
     const auto& process_data = it.value();
-    base::Optional<int64_t> last_loss_moment;
-    base::Optional<int64_t> next_no_loss_moment;
+    std::optional<int64_t> last_loss_moment;
+    std::optional<int64_t> next_no_loss_moment;
     for (int64_t metadata_ts : process_data.metadata_timestamps) {
       // Looks for a matching process descriptor in the [t - 0.2s, t + 0.2s]
       // window. The window size is somewhat arbitrary, and can be changed in
@@ -60,7 +60,7 @@
           // trace.
           next_no_loss_moment = *global_metadata_it;
         } else {
-          next_no_loss_moment = base::nullopt;
+          next_no_loss_moment = std::nullopt;
         }
       }
     }
diff --git a/src/trace_processor/importers/proto/active_chrome_processes_tracker.h b/src/trace_processor/importers/proto/active_chrome_processes_tracker.h
index 3dd1499..b4ebdc1 100644
--- a/src/trace_processor/importers/proto/active_chrome_processes_tracker.h
+++ b/src/trace_processor/importers/proto/active_chrome_processes_tracker.h
@@ -17,9 +17,9 @@
 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_ACTIVE_CHROME_PROCESSES_TRACKER_H_
 #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_ACTIVE_CHROME_PROCESSES_TRACKER_H_
 
+#include <optional>
 #include <set>
 #include <vector>
-#include "perfetto/ext/base/optional.h"
 
 #include "perfetto/ext/base/flat_hash_map.h"
 #include "src/trace_processor/storage/trace_storage.h"
@@ -30,9 +30,9 @@
 
 struct ProcessWithDataLoss {
   UniquePid upid;
-  // If not nullopt, the process data is reliable from this point until
+  // If not std::nullopt, the process data is reliable from this point until
   // the end of the trace.
-  base::Optional<int64_t> reliable_from;
+  std::optional<int64_t> reliable_from;
 };
 
 // Tracks ActiveProcesses metadata packets from ChromeTrackEvent,
diff --git a/src/trace_processor/importers/proto/active_chrome_processes_tracker_unittest.cc b/src/trace_processor/importers/proto/active_chrome_processes_tracker_unittest.cc
index 8917eb5..19a04c2 100644
--- a/src/trace_processor/importers/proto/active_chrome_processes_tracker_unittest.cc
+++ b/src/trace_processor/importers/proto/active_chrome_processes_tracker_unittest.cc
@@ -45,8 +45,8 @@
   tracker.AddActiveProcessMetadata(/*timestamp=*/10, /*upid=*/1);
   tracker.AddActiveProcessMetadata(/*timestamp=*/10, /*upid=*/2);
   EXPECT_THAT(tracker.GetProcessesWithDataLoss(),
-              UnorderedElementsAre(ProcessWithDataLoss{1, base::nullopt},
-                                   ProcessWithDataLoss{2, base::nullopt}));
+              UnorderedElementsAre(ProcessWithDataLoss{1, std::nullopt},
+                                   ProcessWithDataLoss{2, std::nullopt}));
 }
 
 TEST(ActiveChromeProcessesTrackerTest, InexactMatch) {
@@ -104,7 +104,7 @@
   // The second process has data loss till the end of the trace.
   EXPECT_THAT(tracker.GetProcessesWithDataLoss(),
               UnorderedElementsAre(ProcessWithDataLoss{1, 15},
-                                   ProcessWithDataLoss{2, base::nullopt}));
+                                   ProcessWithDataLoss{2, std::nullopt}));
 }
 
 }  // namespace
diff --git a/src/trace_processor/importers/proto/android_camera_event_module.h b/src/trace_processor/importers/proto/android_camera_event_module.h
index 8c0453f..88e7476 100644
--- a/src/trace_processor/importers/proto/android_camera_event_module.h
+++ b/src/trace_processor/importers/proto/android_camera_event_module.h
@@ -18,9 +18,9 @@
 #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_ANDROID_CAMERA_EVENT_MODULE_H_
 
 #include <cstdint>
+#include <optional>
 #include <unordered_map>
 
-#include "perfetto/ext/base/optional.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 #include "src/trace_processor/importers/common/parser_types.h"
 #include "src/trace_processor/importers/proto/proto_importer_module.h"
diff --git a/src/trace_processor/importers/proto/android_probes_parser.cc b/src/trace_processor/importers/proto/android_probes_parser.cc
index 7827d50..754f30a 100644
--- a/src/trace_processor/importers/proto/android_probes_parser.cc
+++ b/src/trace_processor/importers/proto/android_probes_parser.cc
@@ -16,7 +16,8 @@
 
 #include "src/trace_processor/importers/proto/android_probes_parser.h"
 
-#include "perfetto/ext/base/optional.h"
+#include <optional>
+
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/traced/sys_stats_counters.h"
 #include "src/trace_processor/importers/common/args_tracker.h"
@@ -344,19 +345,19 @@
     int32_t cur_mode = static_cast<int32_t>(game_pkg.current_mode());
 
     bool is_standard_mode = false;
-    base::Optional<double> standard_downscale;
-    base::Optional<int32_t> standard_angle;
-    base::Optional<double> standard_fps;
+    std::optional<double> standard_downscale;
+    std::optional<int32_t> standard_angle;
+    std::optional<double> standard_fps;
 
     bool is_performance_mode = false;
-    base::Optional<double> perf_downscale;
-    base::Optional<int32_t> perf_angle;
-    base::Optional<double> perf_fps;
+    std::optional<double> perf_downscale;
+    std::optional<int32_t> perf_angle;
+    std::optional<double> perf_fps;
 
     bool is_battery_mode = false;
-    base::Optional<double> battery_downscale;
-    base::Optional<int32_t> battery_angle;
-    base::Optional<double> battery_fps;
+    std::optional<double> battery_downscale;
+    std::optional<int32_t> battery_angle;
+    std::optional<double> battery_fps;
 
     for (auto mode_it = game_pkg.game_mode_info(); mode_it; ++mode_it) {
       protos::pbzero::AndroidGameInterventionList_GameModeInfo::Decoder
@@ -407,7 +408,7 @@
   for (auto it = properties.values(); it; ++it) {
     protos::pbzero::AndroidSystemProperty::PropertyValue::Decoder kv(*it);
     base::StringView name(kv.name());
-    base::Optional<StringId> mapped_name_id;
+    std::optional<StringId> mapped_name_id;
 
     if (name == "debug.tracing.device_state") {
       auto state = kv.value();
@@ -423,7 +424,7 @@
                name == "debug.tracing.mcc" || name == "debug.tracing.mnc") {
       StringId name_id = context_->storage->InternString(
           name.substr(strlen("debug.tracing.")));
-      base::Optional<int32_t> state =
+      std::optional<int32_t> state =
           base::StringToInt32(kv.value().ToStdString());
       if (state) {
         TrackId track =
@@ -438,7 +439,7 @@
       mapped_name_id = plug_type_id_;
     }
     if (mapped_name_id) {
-      base::Optional<int32_t> state =
+      std::optional<int32_t> state =
           base::StringToInt32(kv.value().ToStdString());
       if (state) {
         TrackId track =
diff --git a/src/trace_processor/importers/proto/android_probes_tracker.h b/src/trace_processor/importers/proto/android_probes_tracker.h
index e5e2469..72ccae2 100644
--- a/src/trace_processor/importers/proto/android_probes_tracker.h
+++ b/src/trace_processor/importers/proto/android_probes_tracker.h
@@ -17,9 +17,9 @@
 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_ANDROID_PROBES_TRACKER_H_
 #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_ANDROID_PROBES_TRACKER_H_
 
+#include <optional>
 #include <set>
 
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_view.h"
 
 #include "src/trace_processor/storage/trace_storage.h"
@@ -66,12 +66,12 @@
     seen_packages_.emplace(std::move(package_name));
   }
 
-  base::Optional<TrackId> GetPowerRailTrack(uint32_t index) {
+  std::optional<TrackId> GetPowerRailTrack(uint32_t index) {
     if (index >= power_rail_tracks_.size())
-      return base::nullopt;
+      return std::nullopt;
     TrackId track_id = power_rail_tracks_[index];
-    return track_id == kInvalidTrackId ? base::nullopt
-                                       : base::make_optional(track_id);
+    return track_id == kInvalidTrackId ? std::nullopt
+                                       : std::make_optional(track_id);
   }
 
   void SetPowerRailTrack(uint32_t index, TrackId track_id) {
@@ -80,12 +80,12 @@
     power_rail_tracks_[index] = track_id;
   }
 
-  base::Optional<EnergyConsumerSpecs> GetEnergyBreakdownDescriptor(
+  std::optional<EnergyConsumerSpecs> GetEnergyBreakdownDescriptor(
       int32_t consumer_id) {
     auto it = energy_consumer_descriptors_.find(consumer_id);
     // Didn't receive the descriptor
     if (it == energy_consumer_descriptors_.end()) {
-      return base::nullopt;
+      return std::nullopt;
     }
     return it->second;
   }
@@ -105,14 +105,14 @@
         EnergyConsumerSpecs{name, type, ordinal};
   }
 
-  base::Optional<EntityStateDescriptor> GetEntityStateDescriptor(
+  std::optional<EntityStateDescriptor> GetEntityStateDescriptor(
       int32_t entity_id,
       int32_t state_id) {
     uint64_t id = EntityStateKey(entity_id, state_id);
     auto it = entity_state_descriptors_.find(id);
     // Didn't receive the descriptor
     if (it == entity_state_descriptors_.end()) {
-      return base::nullopt;
+      return std::nullopt;
     }
     return it->second;
   }
diff --git a/src/trace_processor/importers/proto/chrome_string_lookup.h b/src/trace_processor/importers/proto/chrome_string_lookup.h
index 23f8eca..c1446e5 100644
--- a/src/trace_processor/importers/proto/chrome_string_lookup.h
+++ b/src/trace_processor/importers/proto/chrome_string_lookup.h
@@ -19,9 +19,9 @@
 
 #include <map>
 
+#include <optional>
 #include "src/trace_processor/storage/trace_storage.h"
 
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_view.h"
 
 namespace perfetto {
diff --git a/src/trace_processor/importers/proto/content_analyzer.cc b/src/trace_processor/importers/proto/content_analyzer.cc
index a225f09..0123c44 100644
--- a/src/trace_processor/importers/proto/content_analyzer.cc
+++ b/src/trace_processor/importers/proto/content_analyzer.cc
@@ -70,7 +70,7 @@
         path_ids;
     for (auto sample = annotated_map.value().GetIterator(); sample; ++sample) {
       std::string path_string;
-      base::Optional<tables::ExperimentalProtoPathTable::Id> previous_path_id;
+      std::optional<tables::ExperimentalProtoPathTable::Id> previous_path_id;
       util::SizeProfileComputer::FieldPath path;
       for (const auto& field : sample.key()) {
         if (field.has_field_name()) {
diff --git a/src/trace_processor/importers/proto/frame_timeline_event_parser.cc b/src/trace_processor/importers/proto/frame_timeline_event_parser.cc
index 0b3e84e..9afec02 100644
--- a/src/trace_processor/importers/proto/frame_timeline_event_parser.cc
+++ b/src/trace_processor/importers/proto/frame_timeline_event_parser.cc
@@ -302,7 +302,7 @@
     actual_row.jank_tag = jank_tag_none_id_;
   }
 
-  base::Optional<SliceId> opt_slice_id = context_->slice_tracker->BeginTyped(
+  std::optional<SliceId> opt_slice_id = context_->slice_tracker->BeginTyped(
       context_->storage->mutable_actual_frame_timeline_slice_table(),
       actual_row,
       [this, token, jank_type, present_type, prediction_type,
@@ -508,7 +508,7 @@
       is_buffer = context_->storage->InternString("No");
   }
 
-  base::Optional<SliceId> opt_slice_id = context_->slice_tracker->BeginTyped(
+  std::optional<SliceId> opt_slice_id = context_->slice_tracker->BeginTyped(
       context_->storage->mutable_actual_frame_timeline_slice_table(),
       actual_row,
       [this, jank_type, present_type, token, layer_name_id, display_frame_token,
diff --git a/src/trace_processor/importers/proto/gpu_event_parser.cc b/src/trace_processor/importers/proto/gpu_event_parser.cc
index 377b5eb..56bc165 100644
--- a/src/trace_processor/importers/proto/gpu_event_parser.cc
+++ b/src/trace_processor/importers/proto/gpu_event_parser.cc
@@ -280,17 +280,17 @@
   }
   ++gpu_hw_queue_counter_;
 }
-base::Optional<std::string> GpuEventParser::FindDebugName(
+std::optional<std::string> GpuEventParser::FindDebugName(
     int32_t vk_object_type,
     uint64_t vk_handle) const {
   auto map = debug_marker_names_.find(vk_object_type);
   if (map == debug_marker_names_.end()) {
-    return base::nullopt;
+    return std::nullopt;
   }
 
   auto name = map->second.find(vk_handle);
   if (name == map->second.end()) {
-    return base::nullopt;
+    return std::nullopt;
   } else {
     return name->second;
   }
diff --git a/src/trace_processor/importers/proto/gpu_event_parser.h b/src/trace_processor/importers/proto/gpu_event_parser.h
index b79b65f..e2eaac8 100644
--- a/src/trace_processor/importers/proto/gpu_event_parser.h
+++ b/src/trace_processor/importers/proto/gpu_event_parser.h
@@ -17,9 +17,9 @@
 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_GPU_EVENT_PARSER_H_
 #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_GPU_EVENT_PARSER_H_
 
+#include <optional>
 #include <vector>
 
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_writer.h"
 #include "perfetto/protozero/field.h"
 #include "protos/perfetto/trace/android/gpu_mem_event.pbzero.h"
@@ -82,8 +82,8 @@
   void InsertGpuTrack(
       const protos::pbzero::
           GpuRenderStageEvent_Specifications_Description_Decoder& hw_queue);
-  base::Optional<std::string> FindDebugName(int32_t vk_object_type,
-                                            uint64_t vk_handle) const;
+  std::optional<std::string> FindDebugName(int32_t vk_object_type,
+                                           uint64_t vk_handle) const;
   const StringId ParseRenderSubpasses(
       const protos::pbzero::GpuRenderStageEvent_Decoder& event) const;
 
@@ -94,7 +94,7 @@
   // For GpuRenderStageEvent
   const StringId description_id_;
   const StringId gpu_render_stage_scope_id_;
-  std::vector<perfetto::base::Optional<TrackId>> gpu_hw_queue_ids_;
+  std::vector<std::optional<TrackId>> gpu_hw_queue_ids_;
   size_t gpu_hw_queue_counter_ = 0;
   // Map of stage ID -> pair(stage name, stage description)
   std::vector<std::pair<StringId, StringId>> gpu_render_stage_ids_;
diff --git a/src/trace_processor/importers/proto/graphics_frame_event_parser.cc b/src/trace_processor/importers/proto/graphics_frame_event_parser.cc
index aa6742e..11808a4 100644
--- a/src/trace_processor/importers/proto/graphics_frame_event_parser.cc
+++ b/src/trace_processor/importers/proto/graphics_frame_event_parser.cc
@@ -154,7 +154,7 @@
       row.acquire_to_latch_time = latch_ts - acquire_ts;
       row.latch_to_present_time = timestamp - latch_ts;
     }
-    base::Optional<SliceId> opt_slice_id =
+    std::optional<SliceId> opt_slice_id =
         context_->slice_tracker->ScopedTyped(graphics_frame_slice_table, row);
     if (event.type() == GraphicsFrameEvent::DEQUEUE) {
       if (opt_slice_id) {
diff --git a/src/trace_processor/importers/proto/graphics_frame_event_parser.h b/src/trace_processor/importers/proto/graphics_frame_event_parser.h
index 957e6ad..2d37612 100644
--- a/src/trace_processor/importers/proto/graphics_frame_event_parser.h
+++ b/src/trace_processor/importers/proto/graphics_frame_event_parser.h
@@ -17,9 +17,9 @@
 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_GRAPHICS_FRAME_EVENT_PARSER_H_
 #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_GRAPHICS_FRAME_EVENT_PARSER_H_
 
+#include <optional>
 #include <vector>
 
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_writer.h"
 #include "perfetto/protozero/field.h"
 #include "src/trace_processor/importers/common/args_tracker.h"
diff --git a/src/trace_processor/importers/proto/heap_graph_module.cc b/src/trace_processor/importers/proto/heap_graph_module.cc
index 0fe7506..ad2b806 100644
--- a/src/trace_processor/importers/proto/heap_graph_module.cc
+++ b/src/trace_processor/importers/proto/heap_graph_module.cc
@@ -239,7 +239,7 @@
 
     StringId kind = context_->storage->InternString(
         HeapGraphTypeKindToString(entry.kind()));
-    base::Optional<uint64_t> location_id;
+    std::optional<uint64_t> location_id;
     if (entry.has_location_id())
       location_id = entry.location_id();
 
@@ -289,7 +289,7 @@
 }
 
 void HeapGraphModule::DeobfuscateClass(
-    base::Optional<StringId> package_name_id,
+    std::optional<StringId> package_name_id,
     StringId obfuscated_class_name_id,
     const protos::pbzero::ObfuscatedClass::Decoder& cls) {
   auto* heap_graph_tracker = HeapGraphTracker::GetOrCreate(context_);
@@ -320,7 +320,7 @@
   auto* heap_graph_tracker = HeapGraphTracker::GetOrCreate(context_);
   protos::pbzero::DeobfuscationMapping::Decoder deobfuscation_mapping(
       blob.data, blob.size);
-  base::Optional<StringId> package_name_id;
+  std::optional<StringId> package_name_id;
   if (deobfuscation_mapping.package_name().size > 0) {
     package_name_id = context_->storage->string_pool().GetId(
         deobfuscation_mapping.package_name());
@@ -340,7 +340,7 @@
       // TODO(b/153552977): Remove this work-around for legacy traces.
       // For traces without location information, deobfuscate all matching
       // classes.
-      DeobfuscateClass(base::nullopt, *obfuscated_class_name_id, cls);
+      DeobfuscateClass(std::nullopt, *obfuscated_class_name_id, cls);
       if (package_name_id) {
         DeobfuscateClass(package_name_id, *obfuscated_class_name_id, cls);
       }
diff --git a/src/trace_processor/importers/proto/heap_graph_module.h b/src/trace_processor/importers/proto/heap_graph_module.h
index 502eab1..42665ef 100644
--- a/src/trace_processor/importers/proto/heap_graph_module.h
+++ b/src/trace_processor/importers/proto/heap_graph_module.h
@@ -42,7 +42,7 @@
  private:
   void ParseHeapGraph(uint32_t seq_id, int64_t ts, protozero::ConstBytes);
   void ParseDeobfuscationMapping(protozero::ConstBytes);
-  void DeobfuscateClass(base::Optional<StringPool::Id> package_name_id,
+  void DeobfuscateClass(std::optional<StringPool::Id> package_name_id,
                         StringPool::Id obfuscated_class_id,
                         const protos::pbzero::ObfuscatedClass::Decoder& cls);
 
diff --git a/src/trace_processor/importers/proto/heap_graph_tracker.cc b/src/trace_processor/importers/proto/heap_graph_tracker.cc
index b709d33..e337113 100644
--- a/src/trace_processor/importers/proto/heap_graph_tracker.cc
+++ b/src/trace_processor/importers/proto/heap_graph_tracker.cc
@@ -16,8 +16,9 @@
 
 #include "src/trace_processor/importers/proto/heap_graph_tracker.h"
 
+#include <optional>
+
 #include "perfetto/base/flat_set.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_splitter.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "src/trace_processor/importers/proto/profiler_util.h"
@@ -42,7 +43,7 @@
 void ForReferenceSet(TraceStorage* storage,
                      ObjectTable::ConstRowReference object,
                      F fn) {
-  base::Optional<uint32_t> reference_set_id = object.reference_set_id();
+  std::optional<uint32_t> reference_set_id = object.reference_set_id();
   if (!reference_set_id)
     return;
 
@@ -62,13 +63,13 @@
       *storage->heap_graph_class_table().FindById(object.type_id());
 
   StringId kind = cls_row_ref.kind();
-  base::Optional<StringId> weakref_kind =
+  std::optional<StringId> weakref_kind =
       storage->string_pool().GetId("KIND_WEAK_REFERENCE");
-  base::Optional<StringId> softref_kind =
+  std::optional<StringId> softref_kind =
       storage->string_pool().GetId("KIND_SOFT_REFERENCE");
-  base::Optional<StringId> finalizerref_kind =
+  std::optional<StringId> finalizerref_kind =
       storage->string_pool().GetId("KIND_FINALIZER_REFERENCE");
-  base::Optional<StringId> phantomref_kind =
+  std::optional<StringId> phantomref_kind =
       storage->string_pool().GetId("KIND_PHANTOM_REFERENCE");
 
   if ((weakref_kind && kind == *weakref_kind) ||
@@ -94,7 +95,7 @@
 
 struct ClassDescriptor {
   StringId name;
-  base::Optional<StringId> location;
+  std::optional<StringId> location;
 
   bool operator<(const ClassDescriptor& other) const {
     return std::tie(name, location) < std::tie(other.name, other.location);
@@ -109,16 +110,16 @@
   return {type_row_ref.name(), type_row_ref.location()};
 }
 
-base::Optional<ObjectTable::Id> GetReferredObj(const TraceStorage& storage,
-                                               uint32_t ref_set_id,
-                                               const std::string& field_name) {
+std::optional<ObjectTable::Id> GetReferredObj(const TraceStorage& storage,
+                                              uint32_t ref_set_id,
+                                              const std::string& field_name) {
   const auto& refs_tbl = storage.heap_graph_reference_table();
 
   auto refs_it = refs_tbl.FilterToIterator(
       {refs_tbl.reference_set_id().eq(ref_set_id),
        refs_tbl.field_name().eq(NullTermStringView(field_name))});
   if (!refs_it) {
-    return base::nullopt;
+    return std::nullopt;
   }
   return refs_it.owned_id();
 }
@@ -225,7 +226,7 @@
   }
 }
 
-base::Optional<base::StringView> GetStaticClassTypeName(base::StringView type) {
+std::optional<base::StringView> GetStaticClassTypeName(base::StringView type) {
   static const base::StringView kJavaClassTemplate("java.lang.Class<");
   if (!type.empty() && type.at(type.size() - 1) == '>' &&
       type.substr(0, kJavaClassTemplate.size()) == kJavaClassTemplate) {
@@ -316,10 +317,10 @@
                                             sequence_state->current_ts,
                                             -1,
                                             0,
-                                            /*reference_set_id=*/base::nullopt,
+                                            /*reference_set_id=*/std::nullopt,
                                             /*reachable=*/0,
                                             {},
-                                            /*root_type=*/base::nullopt,
+                                            /*root_type=*/std::nullopt,
                                             /*root_distance*/ -1});
     bool inserted;
     std::tie(ptr, inserted) = sequence_state->object_id_to_db_row.Insert(
@@ -335,7 +336,7 @@
   auto* ptr = sequence_state->type_id_to_db_row.Find(type_id);
   if (!ptr) {
     auto id_and_row =
-        class_table->Insert({StringId(), base::nullopt, base::nullopt});
+        class_table->Insert({StringId(), std::nullopt, std::nullopt});
     bool inserted;
     std::tie(ptr, inserted) = sequence_state->type_id_to_db_row.Insert(
         type_id, id_and_row.row_number);
@@ -377,7 +378,7 @@
   for (size_t i = 0; i < obj.referred_objects.size(); ++i) {
     uint64_t owned_object_id = obj.referred_objects[i];
     // This is true for unset reference fields.
-    base::Optional<ObjectTable::RowReference> owned_row_ref;
+    std::optional<ObjectTable::RowReference> owned_row_ref;
     if (owned_object_id != 0)
       owned_row_ref = GetOrInsertObject(&sequence_state, owned_object_id);
 
@@ -385,11 +386,11 @@
         storage_->mutable_heap_graph_reference_table()->Insert(
             {reference_set_id,
              owner_id,
-             owned_row_ref ? base::make_optional(owned_row_ref->id())
-                           : base::nullopt,
+             owned_row_ref ? std::make_optional(owned_row_ref->id())
+                           : std::nullopt,
              {},
              {},
-             /*deobfuscated_field_name=*/base::nullopt});
+             /*deobfuscated_field_name=*/std::nullopt});
     if (!obj.field_name_ids.empty()) {
       sequence_state.references_for_field_name_id[obj.field_name_ids[i]]
           .push_back(ref_id_and_row.row_number);
@@ -431,7 +432,7 @@
 void HeapGraphTracker::AddInternedType(uint32_t seq_id,
                                        uint64_t intern_id,
                                        StringId strid,
-                                       base::Optional<uint64_t> location_id,
+                                       std::optional<uint64_t> location_id,
                                        uint64_t object_size,
                                        std::vector<uint64_t> field_name_ids,
                                        uint64_t superclass_id,
@@ -534,7 +535,7 @@
   for (const auto& p : sequence_state.interned_types) {
     uint64_t id = p.first;
     const InternedType& interned_type = p.second;
-    base::Optional<StringId> location_name;
+    std::optional<StringId> location_name;
     if (interned_type.location_id) {
       auto it = sequence_state.interned_location_names.find(
           *interned_type.location_id);
@@ -622,9 +623,9 @@
     base::StringView normalized_type =
         NormalizeTypeName(storage_->GetString(interned_type.name));
 
-    base::Optional<StringId> class_package;
+    std::optional<StringId> class_package;
     if (location_name) {
-      base::Optional<std::string> package_name =
+      std::optional<std::string> package_name =
           PackageFromLocation(storage_, storage_->GetString(*location_name));
       if (package_name) {
         class_package = storage_->InternString(base::StringView(*package_name));
@@ -677,10 +678,10 @@
   sequence_state_.erase(seq_id);
 }
 
-base::Optional<ObjectTable::Id> HeapGraphTracker::GetReferenceByFieldName(
+std::optional<ObjectTable::Id> HeapGraphTracker::GetReferenceByFieldName(
     ObjectTable::Id obj,
     StringId field) {
-  base::Optional<ObjectTable::Id> referred;
+  std::optional<ObjectTable::Id> referred;
   auto obj_row_ref = *storage_->heap_graph_object_table().FindById(obj);
   ForReferenceSet(storage_, obj_row_ref,
                   [&](ReferenceTable::RowReference ref) -> bool {
@@ -732,16 +733,16 @@
          objects_tbl.graph_sample_ts().eq(seq.current_ts)});
     for (; obj_it; ++obj_it) {
       ObjectTable::Id cleaner_obj_id = obj_it.id();
-      base::Optional<ObjectTable::Id> referent_id =
+      std::optional<ObjectTable::Id> referent_id =
           GetReferenceByFieldName(cleaner_obj_id, referent_str_id_);
-      base::Optional<ObjectTable::Id> thunk_id =
+      std::optional<ObjectTable::Id> thunk_id =
           GetReferenceByFieldName(cleaner_obj_id, cleaner_thunk_str_id_);
 
       if (!referent_id || !thunk_id) {
         continue;
       }
 
-      base::Optional<ObjectTable::Id> next_id =
+      std::optional<ObjectTable::Id> next_id =
           GetReferenceByFieldName(cleaner_obj_id, cleaner_next_str_id_);
       if (next_id.has_value() && *next_id == cleaner_obj_id) {
         // sun.misc.Cleaner.next points to the sun.misc.Cleaner: this means
@@ -753,7 +754,7 @@
   }
 
   for (const auto& cleaner : cleaners) {
-    base::Optional<ObjectTable::Id> this0 =
+    std::optional<ObjectTable::Id> this0 =
         GetReferenceByFieldName(cleaner.thunk, cleaner_thunk_this0_str_id_);
     if (!this0) {
       continue;
@@ -844,14 +845,14 @@
     ClassTable::Id type_id = object_row_ref.type_id();
 
     auto type_row_ref = *storage->heap_graph_class_table().FindById(type_id);
-    base::Optional<StringId> opt_class_name_id =
+    std::optional<StringId> opt_class_name_id =
         type_row_ref.deobfuscated_name();
     if (!opt_class_name_id) {
       opt_class_name_id = type_row_ref.name();
     }
     PERFETTO_CHECK(opt_class_name_id);
     StringId class_name_id = *opt_class_name_id;
-    base::Optional<StringId> root_type = object_row_ref.root_type();
+    std::optional<StringId> root_type = object_row_ref.root_type();
     if (root_type) {
       class_name_id = storage->InternString(base::StringView(
           storage->GetString(class_name_id).ToStdString() + " [" +
@@ -957,7 +958,7 @@
       alloc_row.cumulative_count = 1;
       alloc_row.size = 1;
       alloc_row.cumulative_size = 1;
-      alloc_row.parent_id = base::nullopt;
+      alloc_row.parent_id = std::nullopt;
       tbl->Insert(alloc_row);
       return tbl;
     }
@@ -994,7 +995,7 @@
   for (size_t i = 1; i < init_path.nodes.size(); ++i) {
     const PathFromRoot::Node& node = init_path.nodes[i];
     PERFETTO_CHECK(node.parent_id < i);
-    base::Optional<FlamegraphId> parent_id;
+    std::optional<FlamegraphId> parent_id;
     if (node.parent_id != 0)
       parent_id = node_to_id[node.parent_id];
     const uint32_t depth = node.depth;
diff --git a/src/trace_processor/importers/proto/heap_graph_tracker.h b/src/trace_processor/importers/proto/heap_graph_tracker.h
index 6fd4fda..c2f184f 100644
--- a/src/trace_processor/importers/proto/heap_graph_tracker.h
+++ b/src/trace_processor/importers/proto/heap_graph_tracker.h
@@ -18,11 +18,11 @@
 #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_HEAP_GRAPH_TRACKER_H_
 
 #include <map>
+#include <optional>
 #include <set>
 #include <utility>
 #include <vector>
 
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_view.h"
 
 #include "protos/perfetto/trace/profiling/heap_graph.pbzero.h"
@@ -64,7 +64,7 @@
                       tables::HeapGraphObjectTable::RowReference,
                       PathFromRoot* path);
 
-base::Optional<base::StringView> GetStaticClassTypeName(base::StringView type);
+std::optional<base::StringView> GetStaticClassTypeName(base::StringView type);
 size_t NumberOfArrays(base::StringView type);
 NormalizedType GetNormalizedType(base::StringView type);
 base::StringView NormalizeTypeName(base::StringView type);
@@ -85,7 +85,7 @@
 
     // If this object is an instance of `libcore.util.NativeAllocationRegistry`,
     // this is the value of its `size` field.
-    base::Optional<int64_t> native_allocation_registry_size;
+    std::optional<int64_t> native_allocation_registry_size;
   };
 
   struct SourceRoot {
@@ -108,7 +108,7 @@
   void AddInternedType(uint32_t seq_id,
                        uint64_t intern_id,
                        StringId strid,
-                       base::Optional<uint64_t> location_id,
+                       std::optional<uint64_t> location_id,
                        uint64_t object_size,
                        std::vector<uint64_t> field_name_ids,
                        uint64_t superclass_id,
@@ -128,7 +128,7 @@
   ~HeapGraphTracker() override;
 
   const std::vector<tables::HeapGraphClassTable::RowNumber>* RowsForType(
-      base::Optional<StringId> package_name,
+      std::optional<StringId> package_name,
       StringId type_name) const {
     auto it = class_to_rows_.find(std::make_pair(package_name, type_name));
     if (it == class_to_rows_.end())
@@ -156,7 +156,7 @@
   };
   struct InternedType {
     StringId name;
-    base::Optional<uint64_t> location_id;
+    std::optional<uint64_t> location_id;
     uint64_t object_size;
     std::vector<uint64_t> field_name_ids;
     uint64_t superclass_id;
@@ -192,7 +192,7 @@
     std::map<tables::HeapGraphClassTable::Id,
              std::vector<tables::HeapGraphObjectTable::RowNumber>>
         deferred_reference_objects_for_type_;
-    base::Optional<uint64_t> prev_index;
+    std::optional<uint64_t> prev_index;
     // For most objects, we need not store the size in the object's message
     // itself, because all instances of the type have the same type. In this
     // case, we defer setting self_size in the table until we process the class
@@ -220,7 +220,7 @@
   bool IsTruncated(UniquePid upid, int64_t ts);
 
   // Returns the object pointed to by `field` in `obj`.
-  base::Optional<tables::HeapGraphObjectTable::Id> GetReferenceByFieldName(
+  std::optional<tables::HeapGraphObjectTable::Id> GetReferenceByFieldName(
       tables::HeapGraphObjectTable::Id obj,
       StringId field);
 
@@ -234,14 +234,14 @@
   TraceStorage* const storage_;
   std::map<uint32_t, SequenceState> sequence_state_;
 
-  std::map<std::pair<base::Optional<StringId>, StringId>,
+  std::map<std::pair<std::optional<StringId>, StringId>,
            std::vector<tables::HeapGraphClassTable::RowNumber>>
       class_to_rows_;
   base::FlatHashMap<StringId,
                     std::vector<tables::HeapGraphReferenceTable::RowNumber>>
       field_to_rows_;
 
-  std::map<std::pair<base::Optional<StringId>, StringId>, StringId>
+  std::map<std::pair<std::optional<StringId>, StringId>, StringId>
       deobfuscation_mapping_;
   std::map<std::pair<UniquePid, int64_t>,
            std::set<tables::HeapGraphObjectTable::RowNumber>>
diff --git a/src/trace_processor/importers/proto/heap_graph_tracker_unittest.cc b/src/trace_processor/importers/proto/heap_graph_tracker_unittest.cc
index 491082a..15be4ac 100644
--- a/src/trace_processor/importers/proto/heap_graph_tracker_unittest.cc
+++ b/src/trace_processor/importers/proto/heap_graph_tracker_unittest.cc
@@ -174,7 +174,7 @@
   const auto& class_table = context.storage->heap_graph_class_table();
   size_t count_bitmaps = 0;
   for (uint32_t obj_row = 0; obj_row < objs_table.row_count(); ++obj_row) {
-    base::Optional<uint32_t> class_row =
+    std::optional<uint32_t> class_row =
         class_table.id().IndexOf(objs_table.type_id()[obj_row]);
     ASSERT_TRUE(class_row.has_value());
     if (context.storage->string_pool().Get(class_table.name()[*class_row]) ==
diff --git a/src/trace_processor/importers/proto/heap_profile_tracker.h b/src/trace_processor/importers/proto/heap_profile_tracker.h
index 03b1d7b..5ed5394 100644
--- a/src/trace_processor/importers/proto/heap_profile_tracker.h
+++ b/src/trace_processor/importers/proto/heap_profile_tracker.h
@@ -17,10 +17,10 @@
 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_HEAP_PROFILE_TRACKER_H_
 #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_HEAP_PROFILE_TRACKER_H_
 
+#include <optional>
 #include <set>
 #include <unordered_map>
 
-#include "perfetto/ext/base/optional.h"
 #include "src/trace_processor/importers/proto/stack_profile_tracker.h"
 #include "src/trace_processor/storage/trace_storage.h"
 
@@ -112,7 +112,7 @@
              tables::HeapProfileAllocationTable::Row>
         free_correction;
 
-    base::Optional<uint64_t> prev_index;
+    std::optional<uint64_t> prev_index;
   };
   std::map<uint32_t, SequenceState> sequence_state_;
   TraceProcessorContext* const context_;
diff --git a/src/trace_processor/importers/proto/heap_profile_tracker_unittest.cc b/src/trace_processor/importers/proto/heap_profile_tracker_unittest.cc
index 0ac2608..558ad91 100644
--- a/src/trace_processor/importers/proto/heap_profile_tracker_unittest.cc
+++ b/src/trace_processor/importers/proto/heap_profile_tracker_unittest.cc
@@ -174,17 +174,17 @@
   EXPECT_EQ(depth[0], 0u);
   EXPECT_EQ(depth[1], 1u);
 
-  EXPECT_EQ(parent_id[0], base::nullopt);
+  EXPECT_EQ(parent_id[0], std::nullopt);
   EXPECT_EQ(parent_id[1], CallsiteId{0});
 
   EXPECT_EQ(frame_id[0], FrameId{0});
   EXPECT_EQ(frame_id[1], FrameId{0});
 }
 
-base::Optional<CallsiteId> FindCallstack(const TraceStorage& storage,
-                                         int64_t depth,
-                                         base::Optional<CallsiteId> parent,
-                                         FrameId frame_id) {
+std::optional<CallsiteId> FindCallstack(const TraceStorage& storage,
+                                        int64_t depth,
+                                        std::optional<CallsiteId> parent,
+                                        FrameId frame_id) {
   const auto& callsites = storage.stack_profile_callsite_table();
   for (uint32_t i = 0; i < callsites.row_count(); ++i) {
     if (callsites.depth()[i] == depth && callsites.parent_id()[i] == parent &&
@@ -192,7 +192,7 @@
       return callsites.id()[i];
     }
   }
-  return base::nullopt;
+  return std::nullopt;
 }
 
 TEST(HeapProfileTrackerTest, SourceMappingPath) {
@@ -224,7 +224,7 @@
   spt->AddMapping(0, mapping);
   hpt->CommitAllocations(kDefaultSequence, spt.get(), nullptr);
   auto foo_bar_id = context.storage->string_pool().GetId("/foo/bar");
-  ASSERT_NE(foo_bar_id, base::nullopt);
+  ASSERT_NE(foo_bar_id, std::nullopt);
   EXPECT_THAT(context.storage->stack_profile_mapping_table().name()[0],
               *foo_bar_id);
 }
@@ -331,12 +331,12 @@
   hpt->CommitAllocations(kDefaultSequence, spt.get(), nullptr);
 
   for (size_t i = 0; i < base::ArraySize(callstacks); ++i) {
-    base::Optional<CallsiteId> parent;
+    std::optional<CallsiteId> parent;
     const SequenceStackProfileTracker::SourceCallstack& callstack =
         callstacks[i];
     for (size_t depth = 0; depth < callstack.size(); ++depth) {
       auto frame_id = spt->GetDatabaseFrameIdForTesting(callstack[depth]);
-      base::Optional<CallsiteId> self = FindCallstack(
+      std::optional<CallsiteId> self = FindCallstack(
           *context.storage, static_cast<int64_t>(depth), parent, frame_id);
       ASSERT_TRUE(self.has_value());
       parent = self;
diff --git a/src/trace_processor/importers/proto/memory_tracker_snapshot_parser.cc b/src/trace_processor/importers/proto/memory_tracker_snapshot_parser.cc
index a308351..c4eeeff 100644
--- a/src/trace_processor/importers/proto/memory_tracker_snapshot_parser.cc
+++ b/src/trace_processor/importers/proto/memory_tracker_snapshot_parser.cc
@@ -228,17 +228,17 @@
     ProcessMemorySnapshotId& proc_snapshot_row_id,
     IdNodeMap& id_node_map) {
   EmitMemorySnapshotNodeRowsRecursively(root_node_graph, std::string(),
-                                        base::nullopt, proc_snapshot_row_id,
+                                        std::nullopt, proc_snapshot_row_id,
                                         id_node_map);
 }
 
 void MemoryTrackerSnapshotParser::EmitMemorySnapshotNodeRowsRecursively(
     GlobalNodeGraph::Node& node,
     const std::string& path,
-    base::Optional<tables::MemorySnapshotNodeTable::Id> parent_node_row_id,
+    std::optional<tables::MemorySnapshotNodeTable::Id> parent_node_row_id,
     ProcessMemorySnapshotId& proc_snapshot_row_id,
     IdNodeMap& id_node_map) {
-  base::Optional<tables::MemorySnapshotNodeTable::Id> node_id;
+  std::optional<tables::MemorySnapshotNodeTable::Id> node_id;
   // Skip emitting the root node into the tables - it is not a real node.
   if (!path.empty()) {
     node_id = EmitNode(node, path, parent_node_row_id, proc_snapshot_row_id,
@@ -257,11 +257,11 @@
   }
 }
 
-base::Optional<tables::MemorySnapshotNodeTable::Id>
+std::optional<tables::MemorySnapshotNodeTable::Id>
 MemoryTrackerSnapshotParser::EmitNode(
     const GlobalNodeGraph::Node& node,
     const std::string& path,
-    base::Optional<tables::MemorySnapshotNodeTable::Id> parent_node_row_id,
+    std::optional<tables::MemorySnapshotNodeTable::Id> parent_node_row_id,
     ProcessMemorySnapshotId& proc_snapshot_row_id,
     IdNodeMap& id_node_map) {
   tables::MemorySnapshotNodeTable::Row node_row;
diff --git a/src/trace_processor/importers/proto/memory_tracker_snapshot_parser.h b/src/trace_processor/importers/proto/memory_tracker_snapshot_parser.h
index 32119c2..deec274 100644
--- a/src/trace_processor/importers/proto/memory_tracker_snapshot_parser.h
+++ b/src/trace_processor/importers/proto/memory_tracker_snapshot_parser.h
@@ -90,7 +90,7 @@
   void EmitMemorySnapshotNodeRowsRecursively(
       GlobalNodeGraph::Node& node,
       const std::string&,
-      base::Optional<tables::MemorySnapshotNodeTable::Id> parent_node_row_id,
+      std::optional<tables::MemorySnapshotNodeTable::Id> parent_node_row_id,
       ProcessMemorySnapshotId& proc_snapshot_row_id,
       IdNodeMap& id_node_map);
 
@@ -99,10 +99,10 @@
   // ProcessMemorySnapshotId |proc_snapshot_row_id|. Generates map of
   // MemoryAllocatorNodeId and MemorySnapshotNodeTable::Id |id_node_map| which
   // is used at time of filling out of MemorySnapshotEdgeTable.
-  base::Optional<tables::MemorySnapshotNodeTable::Id> EmitNode(
+  std::optional<tables::MemorySnapshotNodeTable::Id> EmitNode(
       const GlobalNodeGraph::Node& node,
       const std::string& path,
-      base::Optional<tables::MemorySnapshotNodeTable::Id> parent_node_row_id,
+      std::optional<tables::MemorySnapshotNodeTable::Id> parent_node_row_id,
       ProcessMemorySnapshotId& proc_snapshot_row_id,
       IdNodeMap& id_node_map);
 
diff --git a/src/trace_processor/importers/proto/network_trace_module.cc b/src/trace_processor/importers/proto/network_trace_module.cc
index 1ea1ca7..321152e 100644
--- a/src/trace_processor/importers/proto/network_trace_module.cc
+++ b/src/trace_processor/importers/proto/network_trace_module.cc
@@ -157,7 +157,7 @@
   StringId title_id = kNullStringId;
   if (evt.uid() > 0) {
     const auto& package_list = context_->storage->package_list_table();
-    base::Optional<uint32_t> pkg_row = package_list.uid().IndexOf(evt.uid());
+    std::optional<uint32_t> pkg_row = package_list.uid().IndexOf(evt.uid());
     if (pkg_row) {
       title_id = package_list.package_name()[*pkg_row];
     }
diff --git a/src/trace_processor/importers/proto/packet_sequence_state_generation.h b/src/trace_processor/importers/proto/packet_sequence_state_generation.h
index c3dda05..9c7aa5a 100644
--- a/src/trace_processor/importers/proto/packet_sequence_state_generation.h
+++ b/src/trace_processor/importers/proto/packet_sequence_state_generation.h
@@ -17,9 +17,9 @@
 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PACKET_SEQUENCE_STATE_GENERATION_H_
 #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PACKET_SEQUENCE_STATE_GENERATION_H_
 
+#include <optional>
 #include <unordered_map>
 
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/trace_processor/ref_counted.h"
 #include "src/trace_processor/util/interned_message_view.h"
 
@@ -106,7 +106,7 @@
   PacketSequenceState* state_;
   size_t generation_index_;
   InternedFieldMap interned_data_;
-  base::Optional<InternedMessageView> trace_packet_defaults_;
+  std::optional<InternedMessageView> trace_packet_defaults_;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/proto/perf_sample_tracker.cc b/src/trace_processor/importers/proto/perf_sample_tracker.cc
index aad2013..0105985 100644
--- a/src/trace_processor/importers/proto/perf_sample_tracker.cc
+++ b/src/trace_processor/importers/proto/perf_sample_tracker.cc
@@ -135,7 +135,7 @@
   if (cpu_it != seq_state->per_cpu.end())
     return {seq_state->perf_session_id, cpu_it->second.timebase_track_id};
 
-  base::Optional<PerfSampleDefaults::Decoder> perf_defaults;
+  std::optional<PerfSampleDefaults::Decoder> perf_defaults;
   if (nullable_defaults && nullable_defaults->has_perf_sample_defaults()) {
     perf_defaults.emplace(nullable_defaults->perf_sample_defaults());
   }
diff --git a/src/trace_processor/importers/proto/perf_sample_tracker_unittest.cc b/src/trace_processor/importers/proto/perf_sample_tracker_unittest.cc
index 61819a9..7515a04 100644
--- a/src/trace_processor/importers/proto/perf_sample_tracker_unittest.cc
+++ b/src/trace_processor/importers/proto/perf_sample_tracker_unittest.cc
@@ -213,10 +213,10 @@
 
   EXPECT_NE(stream.perf_session_id, stream2.perf_session_id);
 
-  base::Optional<int64_t> shard_count = context.storage->GetIndexedStats(
+  std::optional<int64_t> shard_count = context.storage->GetIndexedStats(
       stats::perf_process_shard_count,
       static_cast<int>(stream.perf_session_id));
-  base::Optional<int64_t> chosen_shard = context.storage->GetIndexedStats(
+  std::optional<int64_t> chosen_shard = context.storage->GetIndexedStats(
       stats::perf_chosen_process_shard,
       static_cast<int>(stream.perf_session_id));
 
@@ -225,10 +225,10 @@
   ASSERT_TRUE(chosen_shard.has_value());
   EXPECT_EQ(chosen_shard.value(), 7);
 
-  base::Optional<int64_t> shard_count2 = context.storage->GetIndexedStats(
+  std::optional<int64_t> shard_count2 = context.storage->GetIndexedStats(
       stats::perf_process_shard_count,
       static_cast<int>(stream.perf_session_id));
-  base::Optional<int64_t> chosen_shard2 = context.storage->GetIndexedStats(
+  std::optional<int64_t> chosen_shard2 = context.storage->GetIndexedStats(
       stats::perf_chosen_process_shard,
       static_cast<int>(stream.perf_session_id));
 
diff --git a/src/trace_processor/importers/proto/profile_module.cc b/src/trace_processor/importers/proto/profile_module.cc
index ec860da..c337869 100644
--- a/src/trace_processor/importers/proto/profile_module.cc
+++ b/src/trace_processor/importers/proto/profile_module.cc
@@ -251,7 +251,7 @@
       sequence_state->state()->sequence_stack_profile_tracker();
   ProfilePacketInternLookup intern_lookup(sequence_state);
   uint64_t callstack_iid = sample.callstack_iid();
-  base::Optional<CallsiteId> cs_id =
+  std::optional<CallsiteId> cs_id =
       stack_tracker.FindOrInsertCallstack(callstack_iid, &intern_lookup);
 
   // A failed lookup of the interned callstack can mean either:
@@ -285,7 +285,7 @@
   StringPool::Id cpu_mode_id =
       storage->InternString(ProfilePacketUtils::StringifyCpuMode(cpu_mode));
 
-  base::Optional<StringPool::Id> unwind_error_id;
+  std::optional<StringPool::Id> unwind_error_id;
   if (sample.has_unwind_error()) {
     auto unwind_error =
         static_cast<Profiling::StackUnwindError>(sample.unwind_error());
diff --git a/src/trace_processor/importers/proto/profile_packet_utils.h b/src/trace_processor/importers/proto/profile_packet_utils.h
index 683348c..64b2c50 100644
--- a/src/trace_processor/importers/proto/profile_packet_utils.h
+++ b/src/trace_processor/importers/proto/profile_packet_utils.h
@@ -16,8 +16,8 @@
 
 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PROFILE_PACKET_UTILS_H_
 #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PROFILE_PACKET_UTILS_H_
+#include <optional>
 
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_view.h"
 #include "src/trace_processor/importers/proto/packet_sequence_state.h"
 #include "src/trace_processor/importers/proto/stack_profile_tracker.h"
@@ -133,7 +133,7 @@
       : seq_state_(seq_state) {}
   ~ProfilePacketInternLookup() override;
 
-  base::Optional<base::StringView> GetString(
+  std::optional<base::StringView> GetString(
       SequenceStackProfileTracker::SourceStringId iid,
       SequenceStackProfileTracker::InternedStringType type) const override {
     protos::pbzero::InternedString::Decoder* decoder = nullptr;
@@ -155,37 +155,37 @@
         break;
     }
     if (!decoder)
-      return base::nullopt;
+      return std::nullopt;
     return base::StringView(reinterpret_cast<const char*>(decoder->str().data),
                             decoder->str().size);
   }
 
-  base::Optional<SequenceStackProfileTracker::SourceMapping> GetMapping(
+  std::optional<SequenceStackProfileTracker::SourceMapping> GetMapping(
       SequenceStackProfileTracker::SourceMappingId iid) const override {
     auto* decoder = seq_state_->LookupInternedMessage<
         protos::pbzero::InternedData::kMappingsFieldNumber,
         protos::pbzero::Mapping>(iid);
     if (!decoder)
-      return base::nullopt;
+      return std::nullopt;
     return ProfilePacketUtils::MakeSourceMapping(*decoder);
   }
 
-  base::Optional<SequenceStackProfileTracker::SourceFrame> GetFrame(
+  std::optional<SequenceStackProfileTracker::SourceFrame> GetFrame(
       SequenceStackProfileTracker::SourceFrameId iid) const override {
     auto* decoder = seq_state_->LookupInternedMessage<
         protos::pbzero::InternedData::kFramesFieldNumber,
         protos::pbzero::Frame>(iid);
     if (!decoder)
-      return base::nullopt;
+      return std::nullopt;
     return ProfilePacketUtils::MakeSourceFrame(*decoder);
   }
 
-  base::Optional<SequenceStackProfileTracker::SourceCallstack> GetCallstack(
+  std::optional<SequenceStackProfileTracker::SourceCallstack> GetCallstack(
       SequenceStackProfileTracker::SourceCallstackId iid) const override {
     auto* interned_message_view = seq_state_->GetInternedMessageView(
         protos::pbzero::InternedData::kCallstacksFieldNumber, iid);
     if (!interned_message_view)
-      return base::nullopt;
+      return std::nullopt;
     protos::pbzero::Callstack::Decoder decoder(
         interned_message_view->message().data(),
         interned_message_view->message().length());
diff --git a/src/trace_processor/importers/proto/profiler_util.cc b/src/trace_processor/importers/proto/profiler_util.cc
index 315eb45..79a5448 100644
--- a/src/trace_processor/importers/proto/profiler_util.cc
+++ b/src/trace_processor/importers/proto/profiler_util.cc
@@ -15,8 +15,8 @@
  */
 
 #include "src/trace_processor/importers/proto/profiler_util.h"
+#include <optional>
 
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "src/trace_processor/storage/trace_storage.h"
 
@@ -29,32 +29,32 @@
 // * /data/app/~~[randomStringA]/[packageName]-[randomStringB]/base.apk
 // The latter is newer (R+), and was added to avoid leaking package names via
 // mountinfo of incremental apk mounts.
-base::Optional<base::StringView> PackageFromApp(base::StringView location) {
+std::optional<base::StringView> PackageFromApp(base::StringView location) {
   location = location.substr(base::StringView("/data/app/").size());
   size_t start = 0;
   if (location.at(0) == '~') {
     size_t slash = location.find('/');
     if (slash == base::StringView::npos) {
-      return base::nullopt;
+      return std::nullopt;
     }
     start = slash + 1;
   }
   size_t end = location.find('/', start + 1);
   if (end == base::StringView::npos) {
-    return base::nullopt;
+    return std::nullopt;
   }
   location = location.substr(start, end);
   size_t minus = location.find('-');
   if (minus == base::StringView::npos) {
-    return base::nullopt;
+    return std::nullopt;
   }
   return location.substr(0, minus);
 }
 
 }  // namespace
 
-base::Optional<std::string> PackageFromLocation(TraceStorage* storage,
-                                                base::StringView location) {
+std::optional<std::string> PackageFromLocation(TraceStorage* storage,
+                                               base::StringView location) {
   // List of some hardcoded apps that do not follow the scheme used in
   // PackageFromApp. Ask for yours to be added.
   //
@@ -146,12 +146,12 @@
   // Deal with paths to /data/app/...
 
   auto extract_package =
-      [storage](base::StringView path) -> base::Optional<std::string> {
+      [storage](base::StringView path) -> std::optional<std::string> {
     auto package = PackageFromApp(path);
     if (!package) {
       PERFETTO_DLOG("Failed to parse %s", path.ToStdString().c_str());
       storage->IncrementStats(stats::deobfuscate_location_parse_error);
-      return base::nullopt;
+      return std::nullopt;
     }
     return package->ToStdString();
   };
@@ -176,7 +176,7 @@
     return extract_package(data_app_path);
   }
 
-  return base::nullopt;
+  return std::nullopt;
 }
 
 std::string FullyQualifiedDeobfuscatedName(
diff --git a/src/trace_processor/importers/proto/profiler_util.h b/src/trace_processor/importers/proto/profiler_util.h
index 6e9ca99..cbf8ad1 100644
--- a/src/trace_processor/importers/proto/profiler_util.h
+++ b/src/trace_processor/importers/proto/profiler_util.h
@@ -17,9 +17,9 @@
 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PROFILER_UTIL_H_
 #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PROFILER_UTIL_H_
 
+#include <optional>
 #include <string>
 
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_view.h"
 #include "src/trace_processor/storage/trace_storage.h"
 
@@ -32,8 +32,8 @@
     protos::pbzero::ObfuscatedClass::Decoder& cls,
     protos::pbzero::ObfuscatedMember::Decoder& member);
 
-base::Optional<std::string> PackageFromLocation(TraceStorage* storage,
-                                                base::StringView location);
+std::optional<std::string> PackageFromLocation(TraceStorage* storage,
+                                               base::StringView location);
 
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/proto_importer_module.h b/src/trace_processor/importers/proto/proto_importer_module.h
index 3ff56aa..cc2c3a2 100644
--- a/src/trace_processor/importers/proto/proto_importer_module.h
+++ b/src/trace_processor/importers/proto/proto_importer_module.h
@@ -17,8 +17,9 @@
 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PROTO_IMPORTER_MODULE_H_
 #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PROTO_IMPORTER_MODULE_H_
 
+#include <optional>
+
 #include "perfetto/base/status.h"
-#include "perfetto/ext/base/optional.h"
 #include "src/trace_processor/importers/common/trace_parser.h"
 
 namespace perfetto {
@@ -54,8 +55,8 @@
   // Allow auto conversion from util::Status to Handled / Error result.
   ModuleResult(base::Status status)
       : ignored_(false),
-        error_(status.ok() ? base::nullopt
-                           : base::make_optional(status.message())) {}
+        error_(status.ok() ? std::nullopt
+                           : std::make_optional(status.message())) {}
 
   // Constructs a result that indicates the module ignored the packet and is
   // deferring the handling of the packet to other modules.
@@ -88,7 +89,7 @@
       : ignored_(false), error_(error) {}
 
   bool ignored_;
-  base::Optional<std::string> error_;
+  std::optional<std::string> error_;
 };
 
 // Base class for modules.
diff --git a/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc b/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
index ff841aa..7ed4723 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
+++ b/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
@@ -140,9 +140,9 @@
                     int32_t next_prio));
 
   MOCK_METHOD3(PushCounter,
-               base::Optional<CounterId>(int64_t timestamp,
-                                         double value,
-                                         TrackId track_id));
+               std::optional<CounterId>(int64_t timestamp,
+                                        double value,
+                                        TrackId track_id));
 };
 
 class MockProcessTracker : public ProcessTracker {
@@ -152,7 +152,7 @@
 
   MOCK_METHOD4(SetProcessMetadata,
                UniquePid(uint32_t pid,
-                         base::Optional<uint32_t> ppid,
+                         std::optional<uint32_t> ppid,
                          base::StringView process_name,
                          base::StringView cmdline));
 
@@ -195,29 +195,29 @@
       : SliceTracker(context) {}
 
   MOCK_METHOD5(Begin,
-               base::Optional<SliceId>(int64_t timestamp,
-                                       TrackId track_id,
-                                       StringId cat,
-                                       StringId name,
-                                       SetArgsCallback args_callback));
+               std::optional<SliceId>(int64_t timestamp,
+                                      TrackId track_id,
+                                      StringId cat,
+                                      StringId name,
+                                      SetArgsCallback args_callback));
   MOCK_METHOD5(End,
-               base::Optional<SliceId>(int64_t timestamp,
-                                       TrackId track_id,
-                                       StringId cat,
-                                       StringId name,
-                                       SetArgsCallback args_callback));
+               std::optional<SliceId>(int64_t timestamp,
+                                      TrackId track_id,
+                                      StringId cat,
+                                      StringId name,
+                                      SetArgsCallback args_callback));
   MOCK_METHOD6(Scoped,
-               base::Optional<SliceId>(int64_t timestamp,
-                                       TrackId track_id,
-                                       StringId cat,
-                                       StringId name,
-                                       int64_t duration,
-                                       SetArgsCallback args_callback));
+               std::optional<SliceId>(int64_t timestamp,
+                                      TrackId track_id,
+                                      StringId cat,
+                                      StringId name,
+                                      int64_t duration,
+                                      SetArgsCallback args_callback));
   MOCK_METHOD4(StartSlice,
-               base::Optional<SliceId>(int64_t timestamp,
-                                       TrackId track_id,
-                                       SetArgsCallback args_callback,
-                                       std::function<SliceId()> inserter));
+               std::optional<SliceId>(int64_t timestamp,
+                                      TrackId track_id,
+                                      SetArgsCallback args_callback,
+                                      std::function<SliceId()> inserter));
 };
 
 class ProtoTraceParserTest : public ::testing::Test {
diff --git a/src/trace_processor/importers/proto/proto_trace_reader.cc b/src/trace_processor/importers/proto/proto_trace_reader.cc
index f3b06fb..f947c0f 100644
--- a/src/trace_processor/importers/proto/proto_trace_reader.cc
+++ b/src/trace_processor/importers/proto/proto_trace_reader.cc
@@ -16,11 +16,11 @@
 
 #include "src/trace_processor/importers/proto/proto_trace_reader.h"
 
+#include <optional>
 #include <string>
 
 #include "perfetto/base/build_config.h"
 #include "perfetto/base/logging.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/ext/base/utils.h"
 #include "perfetto/protozero/proto_decoder.h"
@@ -365,7 +365,7 @@
   uint32_t snapshot_id = context_->clock_tracker->AddSnapshot(clock_timestamps);
 
   // Add the all the clock snapshots to the clock snapshot table.
-  base::Optional<int64_t> trace_ts_for_check;
+  std::optional<int64_t> trace_ts_for_check;
   for (const auto& clock_timestamp : clock_timestamps) {
     // If the clock is incremental, we need to use 0 to map correctly to
     // |absolute_timestamp|.
@@ -398,7 +398,7 @@
   return util::OkStatus();
 }
 
-base::Optional<StringId> ProtoTraceReader::GetBuiltinClockNameOrNull(
+std::optional<StringId> ProtoTraceReader::GetBuiltinClockNameOrNull(
     int64_t clock_id) {
   switch (clock_id) {
     case protos::pbzero::ClockSnapshot::Clock::REALTIME:
@@ -414,7 +414,7 @@
     case protos::pbzero::ClockSnapshot::Clock::BOOTTIME:
       return context_->storage->InternString("BOOTTIME");
     default:
-      return base::nullopt;
+      return std::nullopt;
   }
 }
 
diff --git a/src/trace_processor/importers/proto/proto_trace_reader.h b/src/trace_processor/importers/proto/proto_trace_reader.h
index 3776358..1c554a6 100644
--- a/src/trace_processor/importers/proto/proto_trace_reader.h
+++ b/src/trace_processor/importers/proto/proto_trace_reader.h
@@ -75,7 +75,7 @@
                          TraceBlobView interned_data);
   void ParseTraceConfig(ConstBytes);
 
-  base::Optional<StringId> GetBuiltinClockNameOrNull(int64_t clock_id);
+  std::optional<StringId> GetBuiltinClockNameOrNull(int64_t clock_id);
 
   PacketSequenceState* GetIncrementalStateForPacketSequence(
       uint32_t sequence_id) {
diff --git a/src/trace_processor/importers/proto/stack_profile_tracker.cc b/src/trace_processor/importers/proto/stack_profile_tracker.cc
index cfe90d2..fa1969b 100644
--- a/src/trace_processor/importers/proto/stack_profile_tracker.cc
+++ b/src/trace_processor/importers/proto/stack_profile_tracker.cc
@@ -46,7 +46,7 @@
   string_map_.emplace(id, str.ToStdString());
 }
 
-base::Optional<MappingId> SequenceStackProfileTracker::AddMapping(
+std::optional<MappingId> SequenceStackProfileTracker::AddMapping(
     SourceMappingId id,
     const SourceMapping& mapping,
     const InternLookup* intern_lookup) {
@@ -69,7 +69,7 @@
   if (!opt_build_id) {
     context_->storage->IncrementStats(stats::stackprofile_invalid_string_id);
     PERFETTO_DLOG("Invalid string.");
-    return base::nullopt;
+    return std::nullopt;
   }
   const StringId raw_build_id = opt_build_id.value();
   NullTermStringView raw_build_id_str =
@@ -101,7 +101,7 @@
 
   tables::StackProfileMappingTable* mappings =
       context_->storage->mutable_stack_profile_mapping_table();
-  base::Optional<MappingId> cur_id;
+  std::optional<MappingId> cur_id;
   auto it = mapping_idx_.find(row);
   if (it != mapping_idx_.end()) {
     cur_id = it->second;
@@ -136,16 +136,16 @@
   return cur_id;
 }
 
-base::Optional<FrameId> SequenceStackProfileTracker::AddFrame(
+std::optional<FrameId> SequenceStackProfileTracker::AddFrame(
     SourceFrameId id,
     const SourceFrame& frame,
     const InternLookup* intern_lookup) {
-  base::Optional<std::string> opt_name = FindOrInsertString(
+  std::optional<std::string> opt_name = FindOrInsertString(
       frame.name_id, intern_lookup, InternedStringType::kFunctionName);
   if (!opt_name) {
     context_->storage->IncrementStats(stats::stackprofile_invalid_string_id);
     PERFETTO_DLOG("Invalid string.");
-    return base::nullopt;
+    return std::nullopt;
   }
   const std::string& name = *opt_name;
   const StringId str_id =
@@ -154,7 +154,7 @@
   auto opt_mapping = FindOrInsertMapping(frame.mapping_id, intern_lookup);
   if (!opt_mapping) {
     context_->storage->IncrementStats(stats::stackprofile_invalid_mapping_id);
-    return base::nullopt;
+    return std::nullopt;
   }
   MappingId mapping_id = *opt_mapping;
   const auto& mappings = context_->storage->stack_profile_mapping_table();
@@ -167,7 +167,7 @@
 
   auto* frames = context_->storage->mutable_stack_profile_frame_table();
 
-  base::Optional<FrameId> cur_id;
+  std::optional<FrameId> cur_id;
   auto it = frame_idx_.find(row);
   if (it != frame_idx_.end()) {
     cur_id = it->second;
@@ -192,7 +192,7 @@
           mapping_id, static_cast<uint64_t>(row.rel_pc), *cur_id);
       if (base::Contains(name, '.')) {
         // Java frames always contain a '.'
-        base::Optional<std::string> package =
+        std::optional<std::string> package =
             PackageFromLocation(context_->storage.get(), mapping_name);
         if (package) {
           NameInPackage nip{str_id, context_->storage->InternString(
@@ -212,19 +212,19 @@
   return cur_id;
 }
 
-base::Optional<CallsiteId> SequenceStackProfileTracker::AddCallstack(
+std::optional<CallsiteId> SequenceStackProfileTracker::AddCallstack(
     SourceCallstackId id,
     const SourceCallstack& frame_ids,
     const InternLookup* intern_lookup) {
   if (frame_ids.empty())
-    return base::nullopt;
+    return std::nullopt;
 
-  base::Optional<CallsiteId> parent_id;
+  std::optional<CallsiteId> parent_id;
   for (uint32_t depth = 0; depth < frame_ids.size(); ++depth) {
     auto opt_frame_id = FindOrInsertFrame(frame_ids[depth], intern_lookup);
     if (!opt_frame_id) {
       context_->storage->IncrementStats(stats::stackprofile_invalid_frame_id);
-      return base::nullopt;
+      return std::nullopt;
     }
     FrameId frame_id = *opt_frame_id;
 
@@ -256,7 +256,7 @@
   return it->second;
 }
 
-base::Optional<StringId> SequenceStackProfileTracker::FindAndInternString(
+std::optional<StringId> SequenceStackProfileTracker::FindAndInternString(
     SourceStringId id,
     const InternLookup* intern_lookup,
     SequenceStackProfileTracker::InternedStringType type) {
@@ -270,7 +270,7 @@
   return context_->storage->InternString(base::StringView(*opt_str));
 }
 
-base::Optional<std::string> SequenceStackProfileTracker::FindOrInsertString(
+std::optional<std::string> SequenceStackProfileTracker::FindOrInsertString(
     SourceStringId id,
     const InternLookup* intern_lookup,
     SequenceStackProfileTracker::InternedStringType type) {
@@ -285,20 +285,20 @@
         context_->storage->IncrementStats(
             stats::stackprofile_invalid_string_id);
         PERFETTO_DLOG("Invalid string.");
-        return base::nullopt;
+        return std::nullopt;
       }
       return str->ToStdString();
     }
-    return base::nullopt;
+    return std::nullopt;
   }
 
   return it->second;
 }
 
-base::Optional<MappingId> SequenceStackProfileTracker::FindOrInsertMapping(
+std::optional<MappingId> SequenceStackProfileTracker::FindOrInsertMapping(
     SourceMappingId mapping_id,
     const InternLookup* intern_lookup) {
-  base::Optional<MappingId> res;
+  std::optional<MappingId> res;
   auto it = mapping_ids_.find(mapping_id);
   if (it == mapping_ids_.end()) {
     if (intern_lookup) {
@@ -315,10 +315,10 @@
   return res;
 }
 
-base::Optional<FrameId> SequenceStackProfileTracker::FindOrInsertFrame(
+std::optional<FrameId> SequenceStackProfileTracker::FindOrInsertFrame(
     SourceFrameId frame_id,
     const InternLookup* intern_lookup) {
-  base::Optional<FrameId> res;
+  std::optional<FrameId> res;
   auto it = frame_ids_.find(frame_id);
   if (it == frame_ids_.end()) {
     if (intern_lookup) {
@@ -337,10 +337,10 @@
   return res;
 }
 
-base::Optional<CallsiteId> SequenceStackProfileTracker::FindOrInsertCallstack(
+std::optional<CallsiteId> SequenceStackProfileTracker::FindOrInsertCallstack(
     SourceCallstackId callstack_id,
     const InternLookup* intern_lookup) {
-  base::Optional<CallsiteId> res;
+  std::optional<CallsiteId> res;
   auto it = callstack_ids_.find(callstack_id);
   if (it == callstack_ids_.end()) {
     auto interned_callstack = intern_lookup->GetCallstack(callstack_id);
diff --git a/src/trace_processor/importers/proto/stack_profile_tracker.h b/src/trace_processor/importers/proto/stack_profile_tracker.h
index c88f2f3..a29d3c1 100644
--- a/src/trace_processor/importers/proto/stack_profile_tracker.h
+++ b/src/trace_processor/importers/proto/stack_profile_tracker.h
@@ -18,9 +18,9 @@
 #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_STACK_PROFILE_TRACKER_H_
 
 #include <deque>
+#include <optional>
 #include <unordered_map>
 
-#include "perfetto/ext/base/optional.h"
 #include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/tables/profiler_tables_py.h"
 
@@ -201,12 +201,12 @@
    public:
     virtual ~InternLookup();
 
-    virtual base::Optional<base::StringView> GetString(
+    virtual std::optional<base::StringView> GetString(
         SourceStringId,
         InternedStringType) const = 0;
-    virtual base::Optional<SourceMapping> GetMapping(SourceMappingId) const = 0;
-    virtual base::Optional<SourceFrame> GetFrame(SourceFrameId) const = 0;
-    virtual base::Optional<SourceCallstack> GetCallstack(
+    virtual std::optional<SourceMapping> GetMapping(SourceMappingId) const = 0;
+    virtual std::optional<SourceFrame> GetFrame(SourceFrameId) const = 0;
+    virtual std::optional<SourceCallstack> GetCallstack(
         SourceCallstackId) const = 0;
   };
 
@@ -214,14 +214,14 @@
   ~SequenceStackProfileTracker();
 
   void AddString(SourceStringId, base::StringView);
-  base::Optional<MappingId> AddMapping(
+  std::optional<MappingId> AddMapping(
       SourceMappingId,
       const SourceMapping&,
       const InternLookup* intern_lookup = nullptr);
-  base::Optional<FrameId> AddFrame(SourceFrameId,
-                                   const SourceFrame&,
-                                   const InternLookup* intern_lookup = nullptr);
-  base::Optional<CallsiteId> AddCallstack(
+  std::optional<FrameId> AddFrame(SourceFrameId,
+                                  const SourceFrame&,
+                                  const InternLookup* intern_lookup = nullptr);
+  std::optional<CallsiteId> AddCallstack(
       SourceCallstackId,
       const SourceCallstack&,
       const InternLookup* intern_lookup = nullptr);
@@ -238,21 +238,20 @@
   // This is to support both ProfilePackets that contain the interned data
   // (for Android Q) and where the interned data is kept globally in
   // InternedData (for versions newer than Q).
-  base::Optional<StringId> FindAndInternString(
+  std::optional<StringId> FindAndInternString(SourceStringId,
+                                              const InternLookup* intern_lookup,
+                                              InternedStringType type);
+  std::optional<std::string> FindOrInsertString(
       SourceStringId,
       const InternLookup* intern_lookup,
       InternedStringType type);
-  base::Optional<std::string> FindOrInsertString(
-      SourceStringId,
-      const InternLookup* intern_lookup,
-      InternedStringType type);
-  base::Optional<MappingId> FindOrInsertMapping(
+  std::optional<MappingId> FindOrInsertMapping(
       SourceMappingId,
       const InternLookup* intern_lookup);
-  base::Optional<FrameId> FindOrInsertFrame(SourceFrameId,
-                                            const InternLookup* intern_lookup);
+  std::optional<FrameId> FindOrInsertFrame(SourceFrameId,
+                                           const InternLookup* intern_lookup);
 
-  base::Optional<CallsiteId> FindOrInsertCallstack(
+  std::optional<CallsiteId> FindOrInsertCallstack(
       SourceCallstackId,
       const InternLookup* intern_lookup);
 
diff --git a/src/trace_processor/importers/proto/statsd_module.cc b/src/trace_processor/importers/proto/statsd_module.cc
index 555ccaa..472506c 100644
--- a/src/trace_processor/importers/proto/statsd_module.cc
+++ b/src/trace_processor/importers/proto/statsd_module.cc
@@ -146,7 +146,7 @@
                                      size_t size,
                                      const char* name) {
   pool_.AddFromFileDescriptorSet(data, size);
-  base::Optional<uint32_t> opt_idx = pool_.FindDescriptorIdx(name);
+  std::optional<uint32_t> opt_idx = pool_.FindDescriptorIdx(name);
   if (opt_idx.has_value()) {
     descriptor_ = &pool_.descriptors()[opt_idx.value()];
   }
@@ -195,7 +195,7 @@
 
   AsyncTrackSetTracker::TrackSetId track_set = InternAsyncTrackSetId();
   TrackId track = context_->async_track_set_tracker->Scoped(track_set, ts, 0);
-  base::Optional<SliceId> opt_slice =
+  std::optional<SliceId> opt_slice =
       context_->slice_tracker->Scoped(ts, track, kNullStringId, atom_name, 0);
   if (!opt_slice) {
     return;
diff --git a/src/trace_processor/importers/proto/statsd_module.h b/src/trace_processor/importers/proto/statsd_module.h
index e5157d2..999a339 100644
--- a/src/trace_processor/importers/proto/statsd_module.h
+++ b/src/trace_processor/importers/proto/statsd_module.h
@@ -18,9 +18,9 @@
 #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_STATSD_MODULE_H_
 
 #include <cstdint>
+#include <optional>
 
 #include "perfetto/ext/base/flat_hash_map.h"
-#include "perfetto/ext/base/optional.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 #include "src/trace_processor/importers/common/async_track_set_tracker.h"
 #include "src/trace_processor/importers/common/trace_parser.h"
@@ -76,7 +76,7 @@
   base::FlatHashMap<uint32_t, StringId> atom_names_;
   PoolAndDescriptor pool_;
   util::ProtoToArgsParser args_parser_;
-  base::Optional<AsyncTrackSetTracker::TrackSetId> track_set_id_;
+  std::optional<AsyncTrackSetTracker::TrackSetId> track_set_id_;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/proto/system_probes_parser.cc b/src/trace_processor/importers/proto/system_probes_parser.cc
index 3a3c438..da175fa 100644
--- a/src/trace_processor/importers/proto/system_probes_parser.cc
+++ b/src/trace_processor/importers/proto/system_probes_parser.cc
@@ -48,7 +48,7 @@
 
 namespace {
 
-base::Optional<int> VersionStringToSdkVersion(const std::string& version) {
+std::optional<int> VersionStringToSdkVersion(const std::string& version) {
   // TODO(lalitm): remove this when the SDK version polling saturates
   // S/T traces in practice.
   if (base::StartsWith(version, "T") || base::StartsWith(version, "S")) {
@@ -82,21 +82,21 @@
   }
   // If we reached this point, we don't know how to parse this version
   // so just return null.
-  return base::nullopt;
+  return std::nullopt;
 }
 
-base::Optional<int> FingerprintToSdkVersion(const std::string& fingerprint) {
+std::optional<int> FingerprintToSdkVersion(const std::string& fingerprint) {
   // Try to parse the SDK version from the fingerprint.
   // Examples of fingerprints:
   // google/shamu/shamu:7.0/NBD92F/3753956:userdebug/dev-keys
   // google/coral/coral:12/SP1A.210812.015/7679548:userdebug/dev-keys
   size_t colon = fingerprint.find(':');
   if (colon == std::string::npos)
-    return base::nullopt;
+    return std::nullopt;
 
   size_t slash = fingerprint.find('/', colon);
   if (slash == std::string::npos)
-    return base::nullopt;
+    return std::nullopt;
 
   std::string version = fingerprint.substr(colon + 1, slash - (colon + 1));
   return VersionStringToSdkVersion(version);
@@ -600,7 +600,7 @@
 
   // If we have the SDK version in the trace directly just use that.
   // Otherwise, try and parse it from the fingerprint.
-  base::Optional<int64_t> opt_sdk_version;
+  std::optional<int64_t> opt_sdk_version;
   if (packet.has_android_sdk_version()) {
     opt_sdk_version = static_cast<int64_t>(packet.android_sdk_version());
   } else if (packet.has_android_build_fingerprint()) {
diff --git a/src/trace_processor/importers/proto/track_event_parser.cc b/src/trace_processor/importers/proto/track_event_parser.cc
index ab74bb1..2373f70 100644
--- a/src/trace_processor/importers/proto/track_event_parser.cc
+++ b/src/trace_processor/importers/proto/track_event_parser.cc
@@ -17,10 +17,10 @@
 #include "src/trace_processor/importers/proto/track_event_parser.h"
 
 #include <iostream>
+#include <optional>
 #include <string>
 
 #include "perfetto/base/logging.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_writer.h"
 #include "perfetto/trace_processor/status.h"
 #include "src/trace_processor/importers/common/args_tracker.h"
@@ -175,7 +175,7 @@
   return result;
 }
 
-base::Optional<base::Status> MaybeParseUnsymbolizedSourceLocation(
+std::optional<base::Status> MaybeParseUnsymbolizedSourceLocation(
     std::string prefix,
     const protozero::Field& field,
     util::ProtoToArgsParser::Delegate& delegate) {
@@ -185,7 +185,7 @@
   if (!decoder) {
     // Lookup failed fall back on default behaviour which will just put
     // the iid into the args table.
-    return base::nullopt;
+    return std::nullopt;
   }
   // Interned mapping_id loses it's meaning when the sequence ends. So we need
   // to get an id from stack_profile_mapping table.
@@ -196,7 +196,7 @@
           ->sequence_stack_profile_tracker()
           .FindOrInsertMapping(decoder->mapping_id(), &intern_lookup);
   if (!mapping_id) {
-    return base::nullopt;
+    return std::nullopt;
   }
   delegate.AddUnsignedInteger(
       util::ProtoToArgsParser::Key(prefix + ".mapping_id"), mapping_id->value);
@@ -205,7 +205,7 @@
   return base::OkStatus();
 }
 
-base::Optional<base::Status> MaybeParseSourceLocation(
+std::optional<base::Status> MaybeParseSourceLocation(
     std::string prefix,
     const protozero::Field& field,
     util::ProtoToArgsParser::Delegate& delegate) {
@@ -214,7 +214,7 @@
   if (!decoder) {
     // Lookup failed fall back on default behaviour which will just put
     // the source_location_iid into the args table.
-    return base::nullopt;
+    return std::nullopt;
   }
 
   delegate.AddString(util::ProtoToArgsParser::Key(prefix + ".file_name"),
@@ -426,7 +426,7 @@
     //      TrackEvent types), or
     //   b) a default track.
     if (track_uuid_) {
-      base::Optional<TrackId> opt_track_id =
+      std::optional<TrackId> opt_track_id =
           track_event_tracker_->GetDescriptorTrack(track_uuid_, name_id_,
                                                    packet_sequence_id_);
       if (!opt_track_id) {
@@ -581,7 +581,7 @@
             track_id_ = context_->track_tracker
                             ->GetOrCreateLegacyChromeGlobalInstantTrack();
             legacy_passthrough_utid_ = utid_;
-            utid_ = base::nullopt;
+            utid_ = std::nullopt;
             break;
           case LegacyEvent::SCOPE_PROCESS:
             if (!upid_) {
@@ -593,7 +593,7 @@
                 context_->track_tracker->InternLegacyChromeProcessInstantTrack(
                     *upid_);
             legacy_passthrough_utid_ = utid_;
-            utid_ = base::nullopt;
+            utid_ = std::nullopt;
             break;
         }
         break;
@@ -697,9 +697,9 @@
     PERFETTO_DCHECK(track_uuid_it);
     PERFETTO_DCHECK(index < TrackEventData::kMaxNumExtraCounters);
 
-    base::Optional<TrackId> track_id = track_event_tracker_->GetDescriptorTrack(
+    std::optional<TrackId> track_id = track_event_tracker_->GetDescriptorTrack(
         *track_uuid_it, kNullStringId, packet_sequence_id_);
-    base::Optional<uint32_t> counter_row =
+    std::optional<uint32_t> counter_row =
         storage_->counter_track_table().id().IndexOf(*track_id);
 
     double value = event_data_->extra_counter_values[index];
@@ -757,12 +757,12 @@
     }
 
     tables::SliceTable::RowReference slice_ref = *opt_thread_slice_ref;
-    base::Optional<int64_t> tts = slice_ref.thread_ts();
+    std::optional<int64_t> tts = slice_ref.thread_ts();
     if (tts) {
       PERFETTO_DCHECK(thread_timestamp_);
       slice_ref.set_thread_dur(*thread_timestamp_ - *tts);
     }
-    base::Optional<int64_t> tic = slice_ref.thread_instruction_count();
+    std::optional<int64_t> tic = slice_ref.thread_instruction_count();
     if (tic) {
       PERFETTO_DCHECK(event_data_->thread_instruction_count);
       slice_ref.set_thread_instruction_delta(
@@ -800,7 +800,7 @@
     return util::OkStatus();
   }
 
-  base::Optional<uint64_t> GetLegacyEventId() {
+  std::optional<uint64_t> GetLegacyEventId() {
     if (legacy_event_.has_unscoped_id())
       return legacy_event_.unscoped_id();
     // TODO(andrewbb): Catapult doesn't support global_id and local_id on flow
@@ -808,7 +808,7 @@
     // to be some callsites supplying local_id in chromium), but we would have
     // to consider the process ID for local IDs and use a separate ID scope for
     // global_id and unscoped_id.
-    return base::nullopt;
+    return std::nullopt;
   }
 
   util::Status ParseFlowEventV1(char phase) {
@@ -902,7 +902,7 @@
     // up nested underneath their parent slices.
     int64_t duration_ns = 0;
     int64_t tidelta = 0;
-    base::Optional<tables::SliceTable::Id> opt_slice_id;
+    std::optional<tables::SliceTable::Id> opt_slice_id;
     auto args_inserter = [this, phase](BoundInserter* inserter) {
       ParseTrackEventArgs(inserter);
       // For legacy MARK event, add phase for JSON exporter.
@@ -1364,9 +1364,9 @@
     row.category = category_id_;
     row.name = name_id_;
     row.thread_ts = thread_timestamp_;
-    row.thread_dur = base::nullopt;
+    row.thread_dur = std::nullopt;
     row.thread_instruction_count = thread_instruction_count_;
-    row.thread_instruction_delta = base::nullopt;
+    row.thread_instruction_delta = std::nullopt;
     return row;
   }
 
@@ -1388,15 +1388,15 @@
   StringId name_id_;
   uint64_t track_uuid_;
   TrackId track_id_;
-  base::Optional<UniqueTid> utid_;
-  base::Optional<UniqueTid> upid_;
-  base::Optional<int64_t> thread_timestamp_;
-  base::Optional<int64_t> thread_instruction_count_;
+  std::optional<UniqueTid> utid_;
+  std::optional<UniqueTid> upid_;
+  std::optional<int64_t> thread_timestamp_;
+  std::optional<int64_t> thread_instruction_count_;
   // All events in legacy JSON require a thread ID, but for some types of
   // events (e.g. async events or process/global-scoped instants), we don't
   // store it in the slice/track model. To pass the utid through to the json
   // export, we store it in an arg.
-  base::Optional<UniqueTid> legacy_passthrough_utid_;
+  std::optional<UniqueTid> legacy_passthrough_utid_;
 
   uint32_t packet_sequence_id_;
 };
@@ -1550,7 +1550,7 @@
                                   util::ProtoToArgsParser::Delegate& delegate) {
         AddActiveProcess(delegate.packet_timestamp(), field.as_int32());
         // Fallthrough so that the parser adds pid as a regular arg.
-        return base::nullopt;
+        return std::nullopt;
       });
 
   for (uint16_t index : kReflectFields) {
diff --git a/src/trace_processor/importers/proto/track_event_tokenizer.cc b/src/trace_processor/importers/proto/track_event_tokenizer.cc
index 35c76a3..6b655f4 100644
--- a/src/trace_processor/importers/proto/track_event_tokenizer.cc
+++ b/src/trace_processor/importers/proto/track_event_tokenizer.cc
@@ -327,7 +327,7 @@
       return;
     }
 
-    base::Optional<double> value;
+    std::optional<double> value;
     if (event.has_counter_value()) {
       value = track_event_tracker_->ConvertToAbsoluteCounterValue(
           track_uuid, packet.trusted_packet_sequence_id(),
@@ -408,7 +408,7 @@
           "Ignoring TrackEvent with more extra_{double_,}counter_values than "
           "TrackEventData::kMaxNumExtraCounters");
     }
-    base::Optional<double> abs_value =
+    std::optional<double> abs_value =
         track_event_tracker_->ConvertToAbsoluteCounterValue(
             *track_uuid_it, trusted_packet_sequence_id,
             static_cast<double>(*value_it));
diff --git a/src/trace_processor/importers/proto/track_event_tracker.cc b/src/trace_processor/importers/proto/track_event_tracker.cc
index f460614..7591028 100644
--- a/src/trace_processor/importers/proto/track_event_tracker.cc
+++ b/src/trace_processor/importers/proto/track_event_tracker.cc
@@ -180,11 +180,11 @@
   return thread_tracks_[utid] = InsertThreadTrack(utid);
 }
 
-base::Optional<TrackId> TrackEventTracker::GetDescriptorTrack(
+std::optional<TrackId> TrackEventTracker::GetDescriptorTrack(
     uint64_t uuid,
     StringId event_name,
-    base::Optional<uint32_t> packet_sequence_id) {
-  base::Optional<TrackId> track_id =
+    std::optional<uint32_t> packet_sequence_id) {
+  std::optional<TrackId> track_id =
       GetDescriptorTrackImpl(uuid, packet_sequence_id);
   if (!track_id || event_name.is_null())
     return track_id;
@@ -208,19 +208,19 @@
   return track_id;
 }
 
-base::Optional<TrackId> TrackEventTracker::GetDescriptorTrackImpl(
+std::optional<TrackId> TrackEventTracker::GetDescriptorTrackImpl(
     uint64_t uuid,
-    base::Optional<uint32_t> packet_sequence_id) {
+    std::optional<uint32_t> packet_sequence_id) {
   auto it = descriptor_tracks_.find(uuid);
   if (it != descriptor_tracks_.end())
     return it->second;
 
-  base::Optional<ResolvedDescriptorTrack> resolved_track =
+  std::optional<ResolvedDescriptorTrack> resolved_track =
       ResolveDescriptorTrack(uuid, nullptr);
   if (!resolved_track)
-    return base::nullopt;
+    return std::nullopt;
 
-  // The reservation must exist as |resolved_track| would have been nullopt
+  // The reservation must exist as |resolved_track| would have been std::nullopt
   // otherwise.
   auto reserved_it = reserved_descriptor_tracks_.find(uuid);
   PERFETTO_CHECK(reserved_it != reserved_descriptor_tracks_.end());
@@ -229,7 +229,7 @@
 
   // We resolve parent_id here to ensure that it's going to be smaller
   // than the id of the child.
-  base::Optional<TrackId> parent_id;
+  std::optional<TrackId> parent_id;
   if (reservation.parent_uuid != 0) {
     parent_id = GetDescriptorTrackImpl(reservation.parent_uuid);
   }
@@ -321,7 +321,7 @@
   PERFETTO_FATAL("For GCC");
 }
 
-base::Optional<TrackEventTracker::ResolvedDescriptorTrack>
+std::optional<TrackEventTracker::ResolvedDescriptorTrack>
 TrackEventTracker::ResolveDescriptorTrack(
     uint64_t uuid,
     std::vector<uint64_t>* descendent_uuids) {
@@ -331,7 +331,7 @@
 
   auto reservation_it = reserved_descriptor_tracks_.find(uuid);
   if (reservation_it == reserved_descriptor_tracks_.end())
-    return base::nullopt;
+    return std::nullopt;
 
   // Resolve process and thread id for tracks produced from within a pid
   // namespace.
@@ -353,16 +353,16 @@
       reservation.pid = *opt_resolved_pid;
   }
 
-  base::Optional<ResolvedDescriptorTrack> resolved_track =
+  std::optional<ResolvedDescriptorTrack> resolved_track =
       ResolveDescriptorTrackImpl(uuid, reservation, descendent_uuids);
   if (!resolved_track) {
-    return base::nullopt;
+    return std::nullopt;
   }
   resolved_descriptor_tracks_[uuid] = *resolved_track;
   return resolved_track;
 }
 
-base::Optional<TrackEventTracker::ResolvedDescriptorTrack>
+std::optional<TrackEventTracker::ResolvedDescriptorTrack>
 TrackEventTracker::ResolveDescriptorTrackImpl(
     uint64_t uuid,
     const DescriptorTrackReservation& reservation,
@@ -370,7 +370,7 @@
   static constexpr size_t kMaxAncestors = 10;
 
   // Try to resolve any parent tracks recursively, too.
-  base::Optional<ResolvedDescriptorTrack> parent_resolved_track;
+  std::optional<ResolvedDescriptorTrack> parent_resolved_track;
   if (reservation.parent_uuid) {
     // Input data may contain loops or extremely long ancestor track chains. To
     // avoid stack overflow in these situations, we keep track of the ancestors
@@ -387,7 +387,7 @@
           "Too many ancestors in parent_track_uuid hierarchy at track %" PRIu64
           " with parent %" PRIu64,
           uuid, reservation.parent_uuid);
-      return base::nullopt;
+      return std::nullopt;
     }
 
     if (std::find(descendent_uuids->begin(), descendent_uuids->end(),
@@ -396,7 +396,7 @@
           "Loop detected in parent_track_uuid hierarchy at track %" PRIu64
           " with parent %" PRIu64,
           uuid, reservation.parent_uuid);
-      return base::nullopt;
+      return std::nullopt;
     }
 
     parent_resolved_track =
@@ -429,7 +429,7 @@
                     *reservation.pid, *reservation.tid, old_uuid, uuid,
                     reservation.min_timestamp);
 
-      utid = context_->process_tracker->StartNewThread(base::nullopt,
+      utid = context_->process_tracker->StartNewThread(std::nullopt,
                                                        *reservation.tid);
 
       // Associate the new thread with its process.
@@ -462,7 +462,7 @@
                     reservation.min_timestamp);
 
       upid = context_->process_tracker->StartNewProcess(
-          base::nullopt, base::nullopt, *reservation.pid, kNullStringId,
+          std::nullopt, std::nullopt, *reservation.pid, kNullStringId,
           ThreadNamePriority::kTrackDescriptor);
 
       descriptor_uuids_by_upid_[upid] = uuid;
@@ -505,7 +505,7 @@
           "Loop detected in parent_track_uuid hierarchy at track %" PRIu64
           " with parent %" PRIu64,
           uuid, kDefaultDescriptorTrackUuid);
-      return base::nullopt;
+      return std::nullopt;
     }
 
     // This track will be implicitly a child of the default global track.
@@ -518,7 +518,7 @@
 TrackId TrackEventTracker::GetOrCreateDefaultDescriptorTrack() {
   // If the default track was already reserved (e.g. because a producer emitted
   // a descriptor for it) or created, resolve and return it.
-  base::Optional<TrackId> track_id =
+  std::optional<TrackId> track_id =
       GetDescriptorTrack(kDefaultDescriptorTrackUuid);
   if (track_id)
     return *track_id;
@@ -529,7 +529,7 @@
   return *GetDescriptorTrack(kDefaultDescriptorTrackUuid);
 }
 
-base::Optional<double> TrackEventTracker::ConvertToAbsoluteCounterValue(
+std::optional<double> TrackEventTracker::ConvertToAbsoluteCounterValue(
     uint64_t counter_track_uuid,
     uint32_t packet_sequence_id,
     double value) {
@@ -537,14 +537,14 @@
   if (reservation_it == reserved_descriptor_tracks_.end()) {
     PERFETTO_DLOG("Unknown counter track with uuid %" PRIu64,
                   counter_track_uuid);
-    return base::nullopt;
+    return std::nullopt;
   }
 
   DescriptorTrackReservation& reservation = reservation_it->second;
   if (!reservation.is_counter) {
     PERFETTO_DLOG("Track with uuid %" PRIu64 " is not a counter track",
                   counter_track_uuid);
-    return base::nullopt;
+    return std::nullopt;
   }
 
   if (reservation.unit_multiplier > 0)
@@ -558,7 +558,7 @@
           " got:%" PRIu32 ")",
           counter_track_uuid, reservation.packet_sequence_id,
           packet_sequence_id);
-      return base::nullopt;
+      return std::nullopt;
     }
 
     reservation.latest_value += value;
diff --git a/src/trace_processor/importers/proto/track_event_tracker.h b/src/trace_processor/importers/proto/track_event_tracker.h
index 58db962..2eb7092 100644
--- a/src/trace_processor/importers/proto/track_event_tracker.h
+++ b/src/trace_processor/importers/proto/track_event_tracker.h
@@ -102,21 +102,21 @@
   // each |uuid| resolves and inserts the track (and its parent tracks,
   // following the parent_uuid chain recursively) based on reservations made for
   // the |uuid|. If the track is a child track and doesn't have a name yet,
-  // updates the track's name to event_name. Returns nullopt if no track for a
-  // descriptor with this |uuid| has been reserved.
+  // updates the track's name to event_name. Returns std::nullopt if no track
+  // for a descriptor with this |uuid| has been reserved.
   // TODO(lalitm): this method needs to be split up and moved back to
   // TrackTracker.
-  base::Optional<TrackId> GetDescriptorTrack(
+  std::optional<TrackId> GetDescriptorTrack(
       uint64_t uuid,
       StringId event_name = kNullStringId,
-      base::Optional<uint32_t> packet_sequence_id = base::nullopt);
+      std::optional<uint32_t> packet_sequence_id = std::nullopt);
 
   // Converts the given counter value to an absolute value in the unit of the
   // counter, applying incremental delta encoding or unit multipliers as
   // necessary. If the counter uses incremental encoding, |packet_sequence_id|
-  // must match the one in its track reservation. Returns base::nullopt if the
+  // must match the one in its track reservation. Returns std::nullopt if the
   // counter track is unknown or an invalid |packet_sequence_id| was passed.
-  base::Optional<double> ConvertToAbsoluteCounterValue(
+  std::optional<double> ConvertToAbsoluteCounterValue(
       uint64_t counter_track_uuid,
       uint32_t packet_sequence_id,
       double value);
@@ -144,15 +144,15 @@
     range_of_interest_start_us_ = range_of_interest_start_us;
   }
 
-  base::Optional<int64_t> range_of_interest_start_us() const {
+  std::optional<int64_t> range_of_interest_start_us() const {
     return range_of_interest_start_us_;
   }
 
  private:
   struct DescriptorTrackReservation {
     uint64_t parent_uuid = 0;
-    base::Optional<uint32_t> pid;
-    base::Optional<uint32_t> tid;
+    std::optional<uint32_t> pid;
+    std::optional<uint32_t> tid;
     int64_t min_timestamp = 0;  // only set if |pid| and/or |tid| is set.
     StringId name = kNullStringId;
     bool use_separate_track = false;
@@ -220,14 +220,14 @@
     UniquePid upid_;
   };
 
-  base::Optional<TrackId> GetDescriptorTrackImpl(
+  std::optional<TrackId> GetDescriptorTrackImpl(
       uint64_t uuid,
-      base::Optional<uint32_t> packet_sequence_id = base::nullopt);
+      std::optional<uint32_t> packet_sequence_id = std::nullopt);
   TrackId CreateTrackFromResolved(const ResolvedDescriptorTrack&);
-  base::Optional<ResolvedDescriptorTrack> ResolveDescriptorTrack(
+  std::optional<ResolvedDescriptorTrack> ResolveDescriptorTrack(
       uint64_t uuid,
       std::vector<uint64_t>* descendent_uuids);
-  base::Optional<ResolvedDescriptorTrack> ResolveDescriptorTrackImpl(
+  std::optional<ResolvedDescriptorTrack> ResolveDescriptorTrackImpl(
       uint64_t uuid,
       const DescriptorTrackReservation&,
       std::vector<uint64_t>* descendent_uuids);
@@ -261,7 +261,7 @@
 
   const StringId default_descriptor_track_name_ = kNullStringId;
 
-  base::Optional<int64_t> range_of_interest_start_us_;
+  std::optional<int64_t> range_of_interest_start_us_;
 
   TraceProcessorContext* const context_;
 };
diff --git a/src/trace_processor/importers/proto/translation_table_module.h b/src/trace_processor/importers/proto/translation_table_module.h
index a180335..a318e60 100644
--- a/src/trace_processor/importers/proto/translation_table_module.h
+++ b/src/trace_processor/importers/proto/translation_table_module.h
@@ -18,8 +18,8 @@
 #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_TRANSLATION_TABLE_MODULE_H_
 
 #include <cstdint>
+#include <optional>
 
-#include "perfetto/ext/base/optional.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 #include "src/trace_processor/importers/proto/proto_importer_module.h"
 #include "src/trace_processor/types/trace_processor_context.h"
diff --git a/src/trace_processor/importers/syscalls/syscall_tracker_unittest.cc b/src/trace_processor/importers/syscalls/syscall_tracker_unittest.cc
index 67ef441..cc0117d 100644
--- a/src/trace_processor/importers/syscalls/syscall_tracker_unittest.cc
+++ b/src/trace_processor/importers/syscalls/syscall_tracker_unittest.cc
@@ -34,17 +34,17 @@
   virtual ~MockSliceTracker() = default;
 
   MOCK_METHOD5(Begin,
-               base::Optional<SliceId>(int64_t timestamp,
-                                       TrackId track_id,
-                                       StringId cat,
-                                       StringId name,
-                                       SetArgsCallback args_callback));
+               std::optional<SliceId>(int64_t timestamp,
+                                      TrackId track_id,
+                                      StringId cat,
+                                      StringId name,
+                                      SetArgsCallback args_callback));
   MOCK_METHOD5(End,
-               base::Optional<SliceId>(int64_t timestamp,
-                                       TrackId track_id,
-                                       StringId cat,
-                                       StringId name,
-                                       SetArgsCallback args_callback));
+               std::optional<SliceId>(int64_t timestamp,
+                                      TrackId track_id,
+                                      StringId cat,
+                                      StringId name,
+                                      SetArgsCallback args_callback));
 };
 
 class SyscallTrackerTest : public ::testing::Test {
@@ -68,9 +68,9 @@
   StringId begin_name = kNullStringId;
   StringId end_name = kNullStringId;
   EXPECT_CALL(*slice_tracker, Begin(100, track, kNullStringId, _, _))
-      .WillOnce(DoAll(SaveArg<3>(&begin_name), Return(base::nullopt)));
+      .WillOnce(DoAll(SaveArg<3>(&begin_name), Return(std::nullopt)));
   EXPECT_CALL(*slice_tracker, End(110, track, kNullStringId, _, _))
-      .WillOnce(DoAll(SaveArg<3>(&end_name), Return(base::nullopt)));
+      .WillOnce(DoAll(SaveArg<3>(&end_name), Return(std::nullopt)));
 
   SyscallTracker* syscall_tracker = SyscallTracker::GetOrCreate(&context);
   syscall_tracker->Enter(100 /*ts*/, 42 /*utid*/, 57 /*sys_read*/);
@@ -84,9 +84,9 @@
   StringId begin_name = kNullStringId;
   StringId end_name = kNullStringId;
   EXPECT_CALL(*slice_tracker, Begin(100, track, kNullStringId, _, _))
-      .WillOnce(DoAll(SaveArg<3>(&begin_name), Return(base::nullopt)));
+      .WillOnce(DoAll(SaveArg<3>(&begin_name), Return(std::nullopt)));
   EXPECT_CALL(*slice_tracker, End(110, track, kNullStringId, _, _))
-      .WillOnce(DoAll(SaveArg<3>(&end_name), Return(base::nullopt)));
+      .WillOnce(DoAll(SaveArg<3>(&end_name), Return(std::nullopt)));
 
   SyscallTracker* syscall_tracker = SyscallTracker::GetOrCreate(&context);
   syscall_tracker->SetArchitecture(kAarch64);
@@ -101,9 +101,9 @@
   StringId begin_name = kNullStringId;
   StringId end_name = kNullStringId;
   EXPECT_CALL(*slice_tracker, Begin(100, track, kNullStringId, _, _))
-      .WillOnce(DoAll(SaveArg<3>(&begin_name), Return(base::nullopt)));
+      .WillOnce(DoAll(SaveArg<3>(&begin_name), Return(std::nullopt)));
   EXPECT_CALL(*slice_tracker, End(110, track, kNullStringId, _, _))
-      .WillOnce(DoAll(SaveArg<3>(&end_name), Return(base::nullopt)));
+      .WillOnce(DoAll(SaveArg<3>(&end_name), Return(std::nullopt)));
 
   SyscallTracker* syscall_tracker = SyscallTracker::GetOrCreate(&context);
   syscall_tracker->SetArchitecture(kX86_64);
diff --git a/src/trace_processor/importers/systrace/systrace_line_parser.cc b/src/trace_processor/importers/systrace/systrace_line_parser.cc
index 150124c..958cb7e 100644
--- a/src/trace_processor/importers/systrace/systrace_line_parser.cc
+++ b/src/trace_processor/importers/systrace/systrace_line_parser.cc
@@ -64,7 +64,7 @@
       ThreadNamePriority::kFtrace);
 
   if (!line.tgid_str.empty() && line.tgid_str != "-----") {
-    base::Optional<uint32_t> tgid = base::StringToUInt32(line.tgid_str);
+    std::optional<uint32_t> tgid = base::StringToUInt32(line.tgid_str);
     if (tgid) {
       context_->process_tracker->UpdateThread(line.pid, tgid.value());
     }
@@ -116,7 +116,7 @@
         line.ts, line.pid, line.args_str.c_str());
   } else if (line.event_name == "sched_waking") {
     auto comm = args["comm"];
-    base::Optional<uint32_t> wakee_pid = base::StringToUInt32(args["pid"]);
+    std::optional<uint32_t> wakee_pid = base::StringToUInt32(args["pid"]);
     if (!wakee_pid.has_value()) {
       return util::Status("Could not convert wakee_pid");
     }
@@ -129,8 +129,8 @@
         line.ts, wakee_utid, utid);
 
   } else if (line.event_name == "cpu_frequency") {
-    base::Optional<uint32_t> event_cpu = base::StringToUInt32(args["cpu_id"]);
-    base::Optional<double> new_state = base::StringToDouble(args["state"]);
+    std::optional<uint32_t> event_cpu = base::StringToUInt32(args["cpu_id"]);
+    std::optional<double> new_state = base::StringToDouble(args["state"]);
     if (!event_cpu.has_value()) {
       return util::Status("Could not convert event cpu");
     }
@@ -142,8 +142,8 @@
         cpufreq_name_id_, event_cpu.value());
     context_->event_tracker->PushCounter(line.ts, new_state.value(), track);
   } else if (line.event_name == "cpu_idle") {
-    base::Optional<uint32_t> event_cpu = base::StringToUInt32(args["cpu_id"]);
-    base::Optional<double> new_state = base::StringToDouble(args["state"]);
+    std::optional<uint32_t> event_cpu = base::StringToUInt32(args["cpu_id"]);
+    std::optional<double> new_state = base::StringToDouble(args["state"]);
     if (!event_cpu.has_value()) {
       return util::Status("Could not convert event cpu");
     }
@@ -261,7 +261,7 @@
       return util::Status("sched_blocked_reason: could not parse io_wait");
     }
     ThreadStateTracker::GetOrCreate(context_)->PushBlockedReason(
-        wakee_utid, static_cast<bool>(*io_wait), base::nullopt);
+        wakee_utid, static_cast<bool>(*io_wait), std::nullopt);
   } else if (line.event_name == "rss_stat") {
     // Format: rss_stat: size=8437760 member=1 curr=1 mm_id=2824390453
     auto size = base::StringToInt64(args["size"]);
@@ -274,9 +274,9 @@
     if (!member.has_value()) {
       return util::Status("rss_stat: could not parse member");
     }
-    base::Optional<bool> curr;
+    std::optional<bool> curr;
     if (!opt_curr.has_value()) {
-      curr = base::make_optional(static_cast<bool>(*opt_curr));
+      curr = std::make_optional(static_cast<bool>(*opt_curr));
     }
     rss_stat_tracker_.ParseRssStat(line.ts, line.pid, *size, *member, curr,
                                    mm_id);
diff --git a/src/trace_processor/importers/systrace/systrace_line_tokenizer.cc b/src/trace_processor/importers/systrace/systrace_line_tokenizer.cc
index e7fa5e8..054bc3e 100644
--- a/src/trace_processor/importers/systrace/systrace_line_tokenizer.cc
+++ b/src/trace_processor/importers/systrace/systrace_line_tokenizer.cc
@@ -78,19 +78,19 @@
   line->event_name = matches[5].str();
   line->args_str = SubstrTrim(matches.suffix());
 
-  base::Optional<uint32_t> maybe_pid = base::StringToUInt32(pid_str);
+  std::optional<uint32_t> maybe_pid = base::StringToUInt32(pid_str);
   if (!maybe_pid.has_value()) {
     return util::Status("Could not convert pid " + pid_str);
   }
   line->pid = maybe_pid.value();
 
-  base::Optional<uint32_t> maybe_cpu = base::StringToUInt32(cpu_str);
+  std::optional<uint32_t> maybe_cpu = base::StringToUInt32(cpu_str);
   if (!maybe_cpu.has_value()) {
     return util::Status("Could not convert cpu " + cpu_str);
   }
   line->cpu = maybe_cpu.value();
 
-  base::Optional<double> maybe_ts = base::StringToDouble(ts_str);
+  std::optional<double> maybe_ts = base::StringToDouble(ts_str);
   if (!maybe_ts.has_value()) {
     return util::Status("Could not convert ts");
   }
diff --git a/src/trace_processor/importers/systrace/systrace_parser.cc b/src/trace_processor/importers/systrace/systrace_parser.cc
index 538278d..ac16506 100644
--- a/src/trace_processor/importers/systrace/systrace_parser.cc
+++ b/src/trace_processor/importers/systrace/systrace_parser.cc
@@ -16,7 +16,8 @@
 
 #include "src/trace_processor/importers/systrace/systrace_parser.h"
 
-#include "perfetto/ext/base/optional.h"
+#include <optional>
+
 #include "perfetto/ext/base/string_utils.h"
 #include "src/trace_processor/importers/common/async_track_set_tracker.h"
 #include "src/trace_processor/importers/common/event_tracker.h"
diff --git a/src/trace_processor/importers/systrace/systrace_trace_parser.cc b/src/trace_processor/importers/systrace/systrace_trace_parser.cc
index 167b3c3..d908d17 100644
--- a/src/trace_processor/importers/systrace/systrace_trace_parser.cc
+++ b/src/trace_processor/importers/systrace/systrace_trace_parser.cc
@@ -143,9 +143,9 @@
                    tokens.size() >= 10) {
           // Format is:
           // user pid ppid vsz rss wchan pc s name my cmd line
-          const base::Optional<uint32_t> pid =
+          const std::optional<uint32_t> pid =
               base::StringToUInt32(tokens[1].ToStdString());
-          const base::Optional<uint32_t> ppid =
+          const std::optional<uint32_t> ppid =
               base::StringToUInt32(tokens[2].ToStdString());
           base::StringView name = tokens[8];
           // Command line may contain spaces, merge all remaining tokens:
@@ -163,9 +163,9 @@
                    tokens.size() >= 4) {
           // Format is:
           // username pid tid my cmd line
-          const base::Optional<uint32_t> tgid =
+          const std::optional<uint32_t> tgid =
               base::StringToUInt32(tokens[1].ToStdString());
-          const base::Optional<uint32_t> tid =
+          const std::optional<uint32_t> tid =
               base::StringToUInt32(tokens[2].ToStdString());
           // Command line may contain spaces, merge all remaining tokens:
           const char* cmd_start = tokens[3].data();
diff --git a/src/trace_processor/iterator_impl.h b/src/trace_processor/iterator_impl.h
index 272d13e..0c8b372 100644
--- a/src/trace_processor/iterator_impl.h
+++ b/src/trace_processor/iterator_impl.h
@@ -20,11 +20,11 @@
 #include <sqlite3.h>
 
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include "perfetto/base/build_config.h"
 #include "perfetto/base/export.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/trace_processor/basic_types.h"
 #include "perfetto/trace_processor/iterator.h"
 #include "perfetto/trace_processor/status.h"
@@ -87,7 +87,7 @@
     int ret = sqlite3_step(*stmt_);
     if (PERFETTO_UNLIKELY(ret != SQLITE_ROW && ret != SQLITE_DONE)) {
       status_ = base::ErrStatus("%s", sqlite_utils::FormatErrorMessage(
-                                          stmt_.get(), base::nullopt, db_, ret)
+                                          stmt_.get(), std::nullopt, db_, ret)
                                           .c_message());
       stmt_.reset();
       return false;
diff --git a/src/trace_processor/metrics/metrics.cc b/src/trace_processor/metrics/metrics.cc
index c713532..34c7863 100644
--- a/src/trace_processor/metrics/metrics.cc
+++ b/src/trace_processor/metrics/metrics.cc
@@ -631,9 +631,9 @@
     if (sqlite3_value_type(argv[i]) != SQLITE_TEXT)
       return base::ErrStatus("RUN_METRIC: all keys must be strings");
 
-    base::Optional<std::string> key_str = sqlite_utils::SqlValueToString(
+    std::optional<std::string> key_str = sqlite_utils::SqlValueToString(
         sqlite_utils::SqliteValueToSqlValue(argv[i]));
-    base::Optional<std::string> value_str = sqlite_utils::SqlValueToString(
+    std::optional<std::string> value_str = sqlite_utils::SqlValueToString(
         sqlite_utils::SqliteValueToSqlValue(argv[i + 1]));
 
     if (!value_str) {
diff --git a/src/trace_processor/metrics/metrics.h b/src/trace_processor/metrics/metrics.h
index 72a10f2..16e004c 100644
--- a/src/trace_processor/metrics/metrics.h
+++ b/src/trace_processor/metrics/metrics.h
@@ -46,12 +46,12 @@
   // Optional because not all protos need to have a field associated with them
   // in the root proto; most files will be just be run using RUN_METRIC by
   // other files.
-  base::Optional<std::string> proto_field_name;
+  std::optional<std::string> proto_field_name;
 
   // The table name which will be created by the SQL below to read the proto
   // bytes from.
   // Should only be set when |proto_field_name| is set.
-  base::Optional<std::string> output_table_name;
+  std::optional<std::string> output_table_name;
 
   // The SQL run by this metric.
   std::string sql;
diff --git a/src/trace_processor/metrics/metrics_unittest.cc b/src/trace_processor/metrics/metrics_unittest.cc
index 29f8f6d..6ba25e2 100644
--- a/src/trace_processor/metrics/metrics_unittest.cc
+++ b/src/trace_processor/metrics/metrics_unittest.cc
@@ -77,7 +77,7 @@
   DescriptorPool pool;
   ProtoDescriptor descriptor("file.proto", ".perfetto.protos",
                              ".perfetto.protos.TestProto",
-                             ProtoDescriptor::Type::kMessage, base::nullopt);
+                             ProtoDescriptor::Type::kMessage, std::nullopt);
   descriptor.AddField(FieldDescriptor(
       "int_value", 1, FieldDescriptorProto::TYPE_INT64, "", false));
 
@@ -100,7 +100,7 @@
   DescriptorPool pool;
   ProtoDescriptor descriptor("file.proto", ".perfetto.protos",
                              ".perfetto.protos.TestProto",
-                             ProtoDescriptor::Type::kMessage, base::nullopt);
+                             ProtoDescriptor::Type::kMessage, std::nullopt);
   descriptor.AddField(FieldDescriptor(
       "double_value", 1, FieldDescriptorProto::TYPE_DOUBLE, "", false));
 
@@ -123,7 +123,7 @@
   DescriptorPool pool;
   ProtoDescriptor descriptor("file.proto", ".perfetto.protos",
                              ".perfetto.protos.TestProto",
-                             ProtoDescriptor::Type::kMessage, base::nullopt);
+                             ProtoDescriptor::Type::kMessage, std::nullopt);
   descriptor.AddField(FieldDescriptor(
       "string_value", 1, FieldDescriptorProto::TYPE_STRING, "", false));
 
@@ -149,13 +149,13 @@
   DescriptorPool pool;
   ProtoDescriptor nested("file.proto", ".perfetto.protos",
                          ".perfetto.protos.TestProto.NestedProto",
-                         ProtoDescriptor::Type::kMessage, base::nullopt);
+                         ProtoDescriptor::Type::kMessage, std::nullopt);
   nested.AddField(FieldDescriptor("nested_int_value", 1,
                                   FieldDescriptorProto::TYPE_INT64, "", false));
 
   ProtoDescriptor descriptor("file.proto", ".perfetto.protos",
                              ".perfetto.protos.TestProto",
-                             ProtoDescriptor::Type::kMessage, base::nullopt);
+                             ProtoDescriptor::Type::kMessage, std::nullopt);
   auto field =
       FieldDescriptor("nested_value", 1, FieldDescriptorProto::TYPE_MESSAGE,
                       ".perfetto.protos.TestProto.NestedProto", false);
@@ -197,7 +197,7 @@
   DescriptorPool pool;
   ProtoDescriptor descriptor("file.proto", ".perfetto.protos",
                              ".perfetto.protos.TestProto",
-                             ProtoDescriptor::Type::kMessage, base::nullopt);
+                             ProtoDescriptor::Type::kMessage, std::nullopt);
   descriptor.AddField(FieldDescriptor(
       "rep_int_value", 1, FieldDescriptorProto::TYPE_INT64, "", true));
 
@@ -235,7 +235,7 @@
   DescriptorPool pool;
   ProtoDescriptor enum_descriptor("file.proto", ".perfetto.protos",
                                   ".perfetto.protos.TestEnum",
-                                  ProtoDescriptor::Type::kEnum, base::nullopt);
+                                  ProtoDescriptor::Type::kEnum, std::nullopt);
   enum_descriptor.AddEnumValue(1, "FIRST");
   enum_descriptor.AddEnumValue(2, "SECOND");
   enum_descriptor.AddEnumValue(3, "THIRD");
@@ -243,7 +243,7 @@
 
   ProtoDescriptor descriptor("file.proto", ".perfetto.protos",
                              ".perfetto.protos.TestMessage",
-                             ProtoDescriptor::Type::kMessage, base::nullopt);
+                             ProtoDescriptor::Type::kMessage, std::nullopt);
   FieldDescriptor enum_field("enum_value", 1, FieldDescriptorProto::TYPE_ENUM,
                              ".perfetto.protos.TestEnum", false);
   enum_field.set_resolved_type_name(".perfetto.protos.TestEnum");
diff --git a/src/trace_processor/prelude/functions/create_function_internal.h b/src/trace_processor/prelude/functions/create_function_internal.h
index 83c0723..1f77459 100644
--- a/src/trace_processor/prelude/functions/create_function_internal.h
+++ b/src/trace_processor/prelude/functions/create_function_internal.h
@@ -18,10 +18,10 @@
 #define SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_CREATE_FUNCTION_INTERNAL_H_
 
 #include <sqlite3.h>
+#include <optional>
 #include <string>
 
 #include "perfetto/base/status.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/trace_processor/basic_types.h"
 #include "src/trace_processor/util/sql_argument.h"
diff --git a/src/trace_processor/prelude/functions/create_view_function.cc b/src/trace_processor/prelude/functions/create_view_function.cc
index e93e8c7..3989de4 100644
--- a/src/trace_processor/prelude/functions/create_view_function.cc
+++ b/src/trace_processor/prelude/functions/create_view_function.cc
@@ -381,7 +381,7 @@
     table_->SetErrorMessage(sqlite3_mprintf(
         "%s: SQLite error while stepping statement: %s",
         table_->prototype_.function_name.c_str(),
-        sqlite_utils::FormatErrorMessage(stmt_, base::nullopt, table_->db_, ret)
+        sqlite_utils::FormatErrorMessage(stmt_, std::nullopt, table_->db_, ret)
             .c_message()));
     return ret;
   }
diff --git a/src/trace_processor/prelude/functions/stack_functions.cc b/src/trace_processor/prelude/functions/stack_functions.cc
index 0774d1d..2f8730b 100644
--- a/src/trace_processor/prelude/functions/stack_functions.cc
+++ b/src/trace_processor/prelude/functions/stack_functions.cc
@@ -21,12 +21,12 @@
 #include <cstring>
 #include <deque>
 #include <iterator>
+#include <optional>
 #include <type_traits>
 #include <vector>
 
 #include "perfetto/base/logging.h"
 #include "perfetto/base/status.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/protozero/scattered_heap_buffer.h"
 #include "perfetto/trace_processor/basic_types.h"
 #include "perfetto/trace_processor/status.h"
diff --git a/src/trace_processor/prelude/functions/utils.h b/src/trace_processor/prelude/functions/utils.h
index b92de3c..c4c5392 100644
--- a/src/trace_processor/prelude/functions/utils.h
+++ b/src/trace_processor/prelude/functions/utils.h
@@ -255,7 +255,7 @@
   uint32_t arg_set_id = static_cast<uint32_t>(sqlite3_value_int(argv[0]));
   const char* key = reinterpret_cast<const char*>(sqlite3_value_text(argv[1]));
 
-  base::Optional<Variadic> opt_value;
+  std::optional<Variadic> opt_value;
   RETURN_IF_ERROR(storage->ExtractArg(arg_set_id, key, &opt_value));
 
   if (!opt_value)
diff --git a/src/trace_processor/prelude/operators/span_join_operator.cc b/src/trace_processor/prelude/operators/span_join_operator.cc
index 61aa3b6..7d4ccb8 100644
--- a/src/trace_processor/prelude/operators/span_join_operator.cc
+++ b/src/trace_processor/prelude/operators/span_join_operator.cc
@@ -43,7 +43,7 @@
   return name == kTsColumnName || name == kDurColumnName;
 }
 
-base::Optional<std::string> HasDuplicateColumns(
+std::optional<std::string> HasDuplicateColumns(
     const std::vector<SqliteTable::Column>& cols) {
   std::set<std::string> names;
   for (const auto& col : cols) {
@@ -51,7 +51,7 @@
       return col.name();
     names.insert(col.name());
   }
-  return base::nullopt;
+  return std::nullopt;
 }
 
 std::string OpToString(int op) {
diff --git a/src/trace_processor/prelude/table_functions/ancestor.cc b/src/trace_processor/prelude/table_functions/ancestor.cc
index 4399397..c1656d9 100644
--- a/src/trace_processor/prelude/table_functions/ancestor.cc
+++ b/src/trace_processor/prelude/table_functions/ancestor.cc
@@ -209,13 +209,13 @@
 }
 
 // static
-base::Optional<std::vector<tables::SliceTable::RowNumber>>
+std::optional<std::vector<tables::SliceTable::RowNumber>>
 Ancestor::GetAncestorSlices(const tables::SliceTable& slices,
                             SliceId slice_id) {
   std::vector<tables::SliceTable::RowNumber> ret;
   auto status = GetAncestors(slices, slice_id, ret);
   if (!status.ok())
-    return base::nullopt;
+    return std::nullopt;
   return std::move(ret);  // -Wreturn-std-move-in-c++11
 }
 
diff --git a/src/trace_processor/prelude/table_functions/ancestor.h b/src/trace_processor/prelude/table_functions/ancestor.h
index eff2c7d..c625f16 100644
--- a/src/trace_processor/prelude/table_functions/ancestor.h
+++ b/src/trace_processor/prelude/table_functions/ancestor.h
@@ -16,8 +16,8 @@
 
 #ifndef SRC_TRACE_PROCESSOR_PRELUDE_TABLE_FUNCTIONS_ANCESTOR_H_
 #define SRC_TRACE_PROCESSOR_PRELUDE_TABLE_FUNCTIONS_ANCESTOR_H_
+#include <optional>
 
-#include "perfetto/ext/base/optional.h"
 #include "src/trace_processor/prelude/table_functions/table_function.h"
 #include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/tables/profiler_tables.h"
@@ -75,9 +75,9 @@
                             std::unique_ptr<Table>& table_return) override;
 
   // Returns a vector of rows numbers which are ancestors of |slice_id|.
-  // Returns base::nullopt if an invalid |slice_id| is given. This is used by
+  // Returns std::nullopt if an invalid |slice_id| is given. This is used by
   // ConnectedFlow to traverse flow indirectly connected flow events.
-  static base::Optional<std::vector<tables::SliceTable::RowNumber>>
+  static std::optional<std::vector<tables::SliceTable::RowNumber>>
   GetAncestorSlices(const tables::SliceTable& slices, SliceId slice_id);
 
  private:
diff --git a/src/trace_processor/prelude/table_functions/descendant.cc b/src/trace_processor/prelude/table_functions/descendant.cc
index 89cd4a2..75cb8c9 100644
--- a/src/trace_processor/prelude/table_functions/descendant.cc
+++ b/src/trace_processor/prelude/table_functions/descendant.cc
@@ -193,13 +193,13 @@
 }
 
 // static
-base::Optional<std::vector<tables::SliceTable::RowNumber>>
+std::optional<std::vector<tables::SliceTable::RowNumber>>
 Descendant::GetDescendantSlices(const tables::SliceTable& slices,
                                 SliceId slice_id) {
   std::vector<tables::SliceTable::RowNumber> ret;
   auto status = GetDescendants(slices, slice_id, ret);
   if (!status.ok())
-    return base::nullopt;
+    return std::nullopt;
   return std::move(ret);
 }
 
diff --git a/src/trace_processor/prelude/table_functions/descendant.h b/src/trace_processor/prelude/table_functions/descendant.h
index d423d07..8e93811 100644
--- a/src/trace_processor/prelude/table_functions/descendant.h
+++ b/src/trace_processor/prelude/table_functions/descendant.h
@@ -17,7 +17,8 @@
 #ifndef SRC_TRACE_PROCESSOR_PRELUDE_TABLE_FUNCTIONS_DESCENDANT_H_
 #define SRC_TRACE_PROCESSOR_PRELUDE_TABLE_FUNCTIONS_DESCENDANT_H_
 
-#include "perfetto/ext/base/optional.h"
+#include <optional>
+
 #include "src/trace_processor/prelude/table_functions/table_function.h"
 #include "src/trace_processor/storage/trace_storage.h"
 
@@ -64,9 +65,9 @@
                             std::unique_ptr<Table>& table_return) override;
 
   // Returns a vector of slice rows which are descendants of |slice_id|. Returns
-  // base::nullopt if an invalid |slice_id| is given. This is used by
+  // std::nullopt if an invalid |slice_id| is given. This is used by
   // ConnectedFlow to traverse flow indirectly connected flow events.
-  static base::Optional<std::vector<tables::SliceTable::RowNumber>>
+  static std::optional<std::vector<tables::SliceTable::RowNumber>>
   GetDescendantSlices(const tables::SliceTable& slices, SliceId slice_id);
 
  private:
diff --git a/src/trace_processor/prelude/table_functions/experimental_annotated_stack.cc b/src/trace_processor/prelude/table_functions/experimental_annotated_stack.cc
index 494931b..8198376 100644
--- a/src/trace_processor/prelude/table_functions/experimental_annotated_stack.cc
+++ b/src/trace_processor/prelude/table_functions/experimental_annotated_stack.cc
@@ -16,7 +16,8 @@
 
 #include "src/trace_processor/prelude/table_functions/experimental_annotated_stack.h"
 
-#include "perfetto/ext/base/optional.h"
+#include <optional>
+
 #include "perfetto/ext/base/string_utils.h"
 #include "src/trace_processor/sqlite/sqlite_utils.h"
 #include "src/trace_processor/storage/trace_storage.h"
@@ -173,7 +174,7 @@
   // entries, each pointing at a frame.
   std::vector<CallsiteTable::RowNumber> cs_rows;
   cs_rows.push_back(opt_start_ref->ToRowNumber());
-  base::Optional<CallsiteId> maybe_parent_id = opt_start_ref->parent_id();
+  std::optional<CallsiteId> maybe_parent_id = opt_start_ref->parent_id();
   while (maybe_parent_id) {
     auto parent_ref = *cs_table.FindById(*maybe_parent_id);
     cs_rows.push_back(parent_ref.ToRowNumber());
diff --git a/src/trace_processor/prelude/table_functions/experimental_flamegraph.cc b/src/trace_processor/prelude/table_functions/experimental_flamegraph.cc
index f3fe53b..ae855be 100644
--- a/src/trace_processor/prelude/table_functions/experimental_flamegraph.cc
+++ b/src/trace_processor/prelude/table_functions/experimental_flamegraph.cc
@@ -119,8 +119,8 @@
     }
   }
 
-  base::Optional<UniquePid> upid;
-  base::Optional<std::string> upid_group;
+  std::optional<UniquePid> upid;
+  std::optional<std::string> upid_group;
   if (upid_it != cs.end()) {
     upid = static_cast<UniquePid>(upid_it->value.AsLong());
   } else {
diff --git a/src/trace_processor/prelude/table_functions/experimental_flamegraph.h b/src/trace_processor/prelude/table_functions/experimental_flamegraph.h
index 8a7e9ae..60792f9 100644
--- a/src/trace_processor/prelude/table_functions/experimental_flamegraph.h
+++ b/src/trace_processor/prelude/table_functions/experimental_flamegraph.h
@@ -34,8 +34,8 @@
     ProfileType profile_type;
     int64_t ts;
     std::vector<TimeConstraints> time_constraints;
-    base::Optional<UniquePid> upid;
-    base::Optional<std::string> upid_group;
+    std::optional<UniquePid> upid;
+    std::optional<std::string> upid_group;
     std::string focus_str;
   };
 
diff --git a/src/trace_processor/prelude/table_functions/experimental_flat_slice.cc b/src/trace_processor/prelude/table_functions/experimental_flat_slice.cc
index 9b38f80..c9bdfed 100644
--- a/src/trace_processor/prelude/table_functions/experimental_flat_slice.cc
+++ b/src/trace_processor/prelude/table_functions/experimental_flat_slice.cc
@@ -98,7 +98,7 @@
     row.category = kNullStringId;
     row.name = kNullStringId;
     row.arg_set_id = kInvalidArgSetId;
-    row.source_id = base::nullopt;
+    row.source_id = std::nullopt;
     row.start_bound = start_bound;
     row.end_bound = end_bound;
     return out->Insert(row).row;
@@ -111,7 +111,7 @@
   };
 
   struct ActiveSlice {
-    base::Optional<uint32_t> source_row;
+    std::optional<uint32_t> source_row;
     uint32_t out_row = std::numeric_limits<uint32_t>::max();
 
     bool is_sentinel() const { return !source_row; }
@@ -185,7 +185,7 @@
       return;
 
     // Otherwise, Add a sentinel slice after the end of the active slice.
-    t.active.source_row = base::nullopt;
+    t.active.source_row = std::nullopt;
     t.active.out_row = insert_sentinel(ts + dur, track_id);
   };
 
diff --git a/src/trace_processor/prelude/table_functions/experimental_flat_slice.h b/src/trace_processor/prelude/table_functions/experimental_flat_slice.h
index aa354c6..0cb10b5 100644
--- a/src/trace_processor/prelude/table_functions/experimental_flat_slice.h
+++ b/src/trace_processor/prelude/table_functions/experimental_flat_slice.h
@@ -17,7 +17,8 @@
 #ifndef SRC_TRACE_PROCESSOR_PRELUDE_TABLE_FUNCTIONS_EXPERIMENTAL_FLAT_SLICE_H_
 #define SRC_TRACE_PROCESSOR_PRELUDE_TABLE_FUNCTIONS_EXPERIMENTAL_FLAT_SLICE_H_
 
-#include "perfetto/ext/base/optional.h"
+#include <optional>
+
 #include "src/trace_processor/prelude/table_functions/table_function.h"
 #include "src/trace_processor/storage/trace_storage.h"
 
diff --git a/src/trace_processor/prelude/table_functions/experimental_sched_upid.cc b/src/trace_processor/prelude/table_functions/experimental_sched_upid.cc
index 54443bb..45a76ea 100644
--- a/src/trace_processor/prelude/table_functions/experimental_sched_upid.cc
+++ b/src/trace_processor/prelude/table_functions/experimental_sched_upid.cc
@@ -22,7 +22,7 @@
 #define PERFETTO_TP_SCHED_UPID_TABLE_DEF(NAME, PARENT, C)     \
   NAME(ExperimentalSchedUpidTable, "experimental_sched_upid") \
   PARENT(PERFETTO_TP_SCHED_SLICE_TABLE_DEF, C)                \
-  C(base::Optional<UniquePid>, upid)
+  C(std::optional<UniquePid>, upid)
 
 PERFETTO_TP_TABLE(PERFETTO_TP_SCHED_UPID_TABLE_DEF);
 
@@ -65,9 +65,9 @@
   return base::OkStatus();
 }
 
-ColumnStorage<base::Optional<UniquePid>>
+ColumnStorage<std::optional<UniquePid>>
 ExperimentalSchedUpid::ComputeUpidColumn() {
-  ColumnStorage<base::Optional<UniquePid>> upid;
+  ColumnStorage<std::optional<UniquePid>> upid;
   for (uint32_t i = 0; i < sched_slice_table_->row_count(); ++i) {
     upid.Append(thread_table_->upid()[sched_slice_table_->utid()[i]]);
   }
diff --git a/src/trace_processor/prelude/table_functions/experimental_sched_upid.h b/src/trace_processor/prelude/table_functions/experimental_sched_upid.h
index 7a9f23d..6837c18 100644
--- a/src/trace_processor/prelude/table_functions/experimental_sched_upid.h
+++ b/src/trace_processor/prelude/table_functions/experimental_sched_upid.h
@@ -41,7 +41,7 @@
                             std::unique_ptr<Table>& table_return) override;
 
  private:
-  ColumnStorage<base::Optional<UniquePid>> ComputeUpidColumn();
+  ColumnStorage<std::optional<UniquePid>> ComputeUpidColumn();
 
   const tables::SchedSliceTable* sched_slice_table_;
   const tables::ThreadTable* thread_table_;
diff --git a/src/trace_processor/prelude/table_functions/experimental_slice_layout.cc b/src/trace_processor/prelude/table_functions/experimental_slice_layout.cc
index 32bf906..e544dbb 100644
--- a/src/trace_processor/prelude/table_functions/experimental_slice_layout.cc
+++ b/src/trace_processor/prelude/table_functions/experimental_slice_layout.cc
@@ -16,7 +16,8 @@
 
 #include "src/trace_processor/prelude/table_functions/experimental_slice_layout.h"
 
-#include "perfetto/ext/base/optional.h"
+#include <optional>
+
 #include "perfetto/ext/base/string_splitter.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "src/trace_processor/sqlite/sqlite_utils.h"
@@ -88,7 +89,7 @@
     if (is_filter_track_ids && is_equal && is_string) {
       filter_string = c.value.AsString();
       for (base::StringSplitter sp(filter_string, ','); sp.Next();) {
-        base::Optional<uint32_t> maybe = base::CStringToUInt32(sp.cur_token());
+        std::optional<uint32_t> maybe = base::CStringToUInt32(sp.cur_token());
         if (maybe) {
           selected_tracks.insert(TrackId{maybe.value()});
         }
@@ -129,7 +130,7 @@
 tables::SliceTable::Id ExperimentalSliceLayout::InsertSlice(
     std::map<tables::SliceTable::Id, tables::SliceTable::Id>& id_map,
     tables::SliceTable::Id id,
-    base::Optional<tables::SliceTable::Id> parent_id) {
+    std::optional<tables::SliceTable::Id> parent_id) {
   if (parent_id) {
     tables::SliceTable::Id root_id = id_map[parent_id.value()];
     id_map[id] = root_id;
diff --git a/src/trace_processor/prelude/table_functions/experimental_slice_layout.h b/src/trace_processor/prelude/table_functions/experimental_slice_layout.h
index 5089e7a..19af803 100644
--- a/src/trace_processor/prelude/table_functions/experimental_slice_layout.h
+++ b/src/trace_processor/prelude/table_functions/experimental_slice_layout.h
@@ -59,7 +59,7 @@
   tables::SliceTable::Id InsertSlice(
       std::map<tables::SliceTable::Id, tables::SliceTable::Id>& id_map,
       tables::SliceTable::Id id,
-      base::Optional<tables::SliceTable::Id> parent_id);
+      std::optional<tables::SliceTable::Id> parent_id);
 
   // TODO(lalitm): remove this cache and move to having explicitly scoped
   // lifetimes of dynamic tables.
diff --git a/src/trace_processor/prelude/table_functions/experimental_slice_layout_unittest.cc b/src/trace_processor/prelude/table_functions/experimental_slice_layout_unittest.cc
index fe2a210..43c986d 100644
--- a/src/trace_processor/prelude/table_functions/experimental_slice_layout_unittest.cc
+++ b/src/trace_processor/prelude/table_functions/experimental_slice_layout_unittest.cc
@@ -72,18 +72,17 @@
       << "Actual:" << actual << "\nExpected:" << expected;
 }
 
-tables::SliceTable::Id Insert(
-    tables::SliceTable* table,
-    int64_t ts,
-    int64_t dur,
-    uint32_t track_id,
-    StringId name,
-    base::Optional<tables::SliceTable::Id> parent_id) {
+tables::SliceTable::Id Insert(tables::SliceTable* table,
+                              int64_t ts,
+                              int64_t dur,
+                              uint32_t track_id,
+                              StringId name,
+                              std::optional<tables::SliceTable::Id> parent_id) {
   tables::SliceTable::Row row;
   row.ts = ts;
   row.dur = dur;
   row.depth = 0;
-  base::Optional<tables::SliceTable::Id> id = parent_id;
+  std::optional<tables::SliceTable::Id> id = parent_id;
   while (id) {
     row.depth++;
     id = table->parent_id()[id.value().value];
@@ -100,7 +99,7 @@
   StringId name = pool.InternString("SingleRow");
 
   Insert(&slice_table, 1 /*ts*/, 5 /*dur*/, 1 /*track_id*/, name,
-         base::nullopt /*parent*/);
+         std::nullopt /*parent*/);
 
   ExperimentalSliceLayout gen(&pool, &slice_table);
 
@@ -120,7 +119,7 @@
   StringId name = pool.InternString("SingleRow");
 
   auto id = Insert(&slice_table, 1 /*ts*/, 5 /*dur*/, 1 /*track_id*/, name,
-                   base::nullopt);
+                   std::nullopt);
   Insert(&slice_table, 1 /*ts*/, 5 /*dur*/, 1 /*track_id*/, name, id);
 
   ExperimentalSliceLayout gen(&pool, &slice_table);
@@ -142,7 +141,7 @@
   StringId name = pool.InternString("MultipleRows");
 
   auto a = Insert(&slice_table, 1 /*ts*/, 5 /*dur*/, 1 /*track_id*/, name,
-                  base::nullopt);
+                  std::nullopt);
   auto b = Insert(&slice_table, 1 /*ts*/, 4 /*dur*/, 1 /*track_id*/, name, a);
   auto c = Insert(&slice_table, 1 /*ts*/, 3 /*dur*/, 1 /*track_id*/, name, b);
   auto d = Insert(&slice_table, 1 /*ts*/, 2 /*dur*/, 1 /*track_id*/, name, c);
@@ -174,10 +173,10 @@
   StringId name4 = pool.InternString("Track4");
 
   auto a = Insert(&slice_table, 1 /*ts*/, 4 /*dur*/, 1 /*track_id*/, name1,
-                  base::nullopt);
+                  std::nullopt);
   auto b = Insert(&slice_table, 1 /*ts*/, 2 /*dur*/, 1 /*track_id*/, name2, a);
   auto x = Insert(&slice_table, 4 /*ts*/, 4 /*dur*/, 2 /*track_id*/, name3,
-                  base::nullopt);
+                  std::nullopt);
   auto y = Insert(&slice_table, 4 /*ts*/, 2 /*dur*/, 2 /*track_id*/, name4, x);
   base::ignore_result(b);
   base::ignore_result(y);
@@ -208,13 +207,13 @@
   StringId name6 = pool.InternString("Slice6");
 
   auto a = Insert(&slice_table, 0 /*ts*/, 4 /*dur*/, 1 /*track_id*/, name1,
-                  base::nullopt);
+                  std::nullopt);
   auto b = Insert(&slice_table, 0 /*ts*/, 2 /*dur*/, 1 /*track_id*/, name2, a);
   auto p = Insert(&slice_table, 3 /*ts*/, 4 /*dur*/, 2 /*track_id*/, name3,
-                  base::nullopt);
+                  std::nullopt);
   auto q = Insert(&slice_table, 3 /*ts*/, 2 /*dur*/, 2 /*track_id*/, name4, p);
   auto x = Insert(&slice_table, 5 /*ts*/, 4 /*dur*/, 1 /*track_id*/, name5,
-                  base::nullopt);
+                  std::nullopt);
   auto y = Insert(&slice_table, 5 /*ts*/, 2 /*dur*/, 1 /*track_id*/, name6, x);
   base::ignore_result(b);
   base::ignore_result(q);
@@ -246,20 +245,20 @@
 
   // Group 1 exists just to create push group 2 down one row.
   auto a = Insert(&slice_table, 0 /*ts*/, 1 /*dur*/, 1 /*track_id*/, name,
-                  base::nullopt);
+                  std::nullopt);
   base::ignore_result(a);
 
   // Group 2 has a depth of 2 so it theoretically "nests" inside a group of
   // depth 4.
   auto c = Insert(&slice_table, 0 /*ts*/, 10 /*dur*/, 2 /*track_id*/, name,
-                  base::nullopt);
+                  std::nullopt);
   auto d = Insert(&slice_table, 0 /*ts*/, 9 /*dur*/, 2 /*track_id*/, name, c);
   base::ignore_result(d);
 
   // Group 3 has a depth of 4 so it could cause group 2 to "nest" if our
   // layout algorithm did not work correctly.
   auto p = Insert(&slice_table, 3 /*ts*/, 4 /*dur*/, 3 /*track_id*/, name,
-                  base::nullopt);
+                  std::nullopt);
   auto q = Insert(&slice_table, 3 /*ts*/, 3 /*dur*/, 3 /*track_id*/, name, p);
   auto r = Insert(&slice_table, 3 /*ts*/, 2 /*dur*/, 3 /*track_id*/, name, q);
   auto s = Insert(&slice_table, 3 /*ts*/, 1 /*dur*/, 3 /*track_id*/, name, r);
@@ -293,14 +292,14 @@
   StringId name5 = pool.InternString("Slice5");
 
   auto a = Insert(&slice_table, 0 /*ts*/, 4 /*dur*/, 1 /*track_id*/, name1,
-                  base::nullopt);
+                  std::nullopt);
   auto b = Insert(&slice_table, 0 /*ts*/, 2 /*dur*/, 1 /*track_id*/, name2, a);
   auto p = Insert(&slice_table, 3 /*ts*/, 4 /*dur*/, 2 /*track_id*/, name3,
-                  base::nullopt);
+                  std::nullopt);
   auto q = Insert(&slice_table, 3 /*ts*/, 2 /*dur*/, 2 /*track_id*/, name4, p);
   // This slice should be ignored as it's not in the filter below:
   Insert(&slice_table, 0 /*ts*/, 9 /*dur*/, 3 /*track_id*/, name5,
-         base::nullopt);
+         std::nullopt);
   base::ignore_result(b);
   base::ignore_result(q);
 
diff --git a/src/trace_processor/prelude/table_functions/flamegraph_construction_algorithms.cc b/src/trace_processor/prelude/table_functions/flamegraph_construction_algorithms.cc
index cd49b14..7424134 100644
--- a/src/trace_processor/prelude/table_functions/flamegraph_construction_algorithms.cc
+++ b/src/trace_processor/prelude/table_functions/flamegraph_construction_algorithms.cc
@@ -29,9 +29,9 @@
 struct MergedCallsite {
   StringId frame_name;
   StringId mapping_name;
-  base::Optional<StringId> source_file;
-  base::Optional<uint32_t> line_number;
-  base::Optional<uint32_t> parent_idx;
+  std::optional<StringId> source_file;
+  std::optional<uint32_t> line_number;
+  std::optional<uint32_t> parent_idx;
   bool operator<(const MergedCallsite& o) const {
     return std::tie(frame_name, mapping_name, parent_idx) <
            std::tie(o.frame_name, o.mapping_name, o.parent_idx);
@@ -60,15 +60,14 @@
       *mapping_tbl.id().IndexOf(frames_tbl.mapping()[frame_idx]);
   StringId mapping_name = mapping_tbl.name()[mapping_idx];
 
-  base::Optional<uint32_t> symbol_set_id =
-      frames_tbl.symbol_set_id()[frame_idx];
+  std::optional<uint32_t> symbol_set_id = frames_tbl.symbol_set_id()[frame_idx];
 
   if (!symbol_set_id) {
     StringId frame_name = frames_tbl.name()[frame_idx];
-    base::Optional<StringId> deobfuscated_name =
+    std::optional<StringId> deobfuscated_name =
         frames_tbl.deobfuscated_name()[frame_idx];
     return {{deobfuscated_name ? *deobfuscated_name : frame_name, mapping_name,
-             base::nullopt, base::nullopt, base::nullopt}};
+             std::nullopt, std::nullopt, std::nullopt}};
   }
 
   std::vector<MergedCallsite> result;
@@ -82,7 +81,7 @@
        ++i) {
     result.emplace_back(MergedCallsite{
         symbols_tbl.name()[i], mapping_name, symbols_tbl.source_file()[i],
-        symbols_tbl.line_number()[i], base::nullopt});
+        symbols_tbl.line_number()[i], std::nullopt});
   }
   std::reverse(result.begin(), result.end());
   return result;
@@ -91,8 +90,8 @@
 
 static FlamegraphTableAndMergedCallsites BuildFlamegraphTableTreeStructure(
     TraceStorage* storage,
-    base::Optional<UniquePid> upid,
-    base::Optional<std::string> upid_group,
+    std::optional<UniquePid> upid,
+    std::optional<std::string> upid_group,
     int64_t default_timestamp,
     StringId profile_type) {
   const tables::StackProfileCallsiteTable& callsites_tbl =
@@ -110,7 +109,7 @@
   // Aggregate callstacks by frame name / mapping name. Use symbolization
   // data.
   for (uint32_t i = 0; i < callsites_tbl.row_count(); ++i) {
-    base::Optional<uint32_t> parent_idx;
+    std::optional<uint32_t> parent_idx;
 
     auto opt_parent_id = callsites_tbl.parent_id()[i];
     if (opt_parent_id) {
@@ -136,7 +135,7 @@
           row.parent_id = tbl->id()[*parent_idx];
         } else {
           row.depth = 0;
-          row.parent_id = base::nullopt;
+          row.parent_id = std::nullopt;
         }
 
         // The 'ts' column is given a default value, taken from the query.
@@ -170,10 +169,10 @@
         MergedCallsite saved_callsite = it->first;
         callsites_to_rowid.erase(saved_callsite);
         if (saved_callsite.source_file != merged_callsite.source_file) {
-          saved_callsite.source_file = base::nullopt;
+          saved_callsite.source_file = std::nullopt;
         }
         if (saved_callsite.line_number != merged_callsite.line_number) {
-          saved_callsite.line_number = base::nullopt;
+          saved_callsite.line_number = std::nullopt;
         }
         callsites_to_rowid[saved_callsite] = it->second;
       }
@@ -335,7 +334,7 @@
   }
   StringId profile_type = storage->InternString("native");
   FlamegraphTableAndMergedCallsites table_and_callsites =
-      BuildFlamegraphTableTreeStructure(storage, upid, base::nullopt, timestamp,
+      BuildFlamegraphTableTreeStructure(storage, upid, std::nullopt, timestamp,
                                         profile_type);
   return BuildFlamegraphTableHeapSizeAndCount(
       std::move(table_and_callsites.tbl),
@@ -345,8 +344,8 @@
 std::unique_ptr<tables::ExperimentalFlamegraphNodesTable>
 BuildNativeCallStackSamplingFlamegraph(
     TraceStorage* storage,
-    base::Optional<UniquePid> upid,
-    base::Optional<std::string> upid_group,
+    std::optional<UniquePid> upid,
+    std::optional<std::string> upid_group,
     const std::vector<TimeConstraints>& time_constraints) {
   // 1.Extract required upids from input.
   std::unordered_set<UniquePid> upids;
@@ -354,7 +353,7 @@
     upids.insert(*upid);
   } else {
     for (base::StringSplitter sp(*upid_group, ','); sp.Next();) {
-      base::Optional<uint32_t> maybe = base::CStringToUInt32(sp.cur_token());
+      std::optional<uint32_t> maybe = base::CStringToUInt32(sp.cur_token());
       if (maybe) {
         upids.insert(*maybe);
       }
@@ -365,7 +364,7 @@
   std::set<tables::ThreadTable::Id> utids;
   RowMap threads_in_pid_rm;
   for (uint32_t i = 0; i < storage->thread_table().row_count(); ++i) {
-    base::Optional<uint32_t> row_upid = storage->thread_table().upid()[i];
+    std::optional<uint32_t> row_upid = storage->thread_table().upid()[i];
     if (row_upid && upids.count(*row_upid) > 0) {
       threads_in_pid_rm.Insert(i);
     }
diff --git a/src/trace_processor/prelude/table_functions/flamegraph_construction_algorithms.h b/src/trace_processor/prelude/table_functions/flamegraph_construction_algorithms.h
index fdda131..82c79b8 100644
--- a/src/trace_processor/prelude/table_functions/flamegraph_construction_algorithms.h
+++ b/src/trace_processor/prelude/table_functions/flamegraph_construction_algorithms.h
@@ -36,8 +36,8 @@
 std::unique_ptr<tables::ExperimentalFlamegraphNodesTable>
 BuildNativeCallStackSamplingFlamegraph(
     TraceStorage* storage,
-    base::Optional<UniquePid> upid,
-    base::Optional<std::string> upid_group,
+    std::optional<UniquePid> upid,
+    std::optional<std::string> upid_group,
     const std::vector<TimeConstraints>& time_constraints);
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/rpc/httpd.cc b/src/trace_processor/rpc/httpd.cc
index 97bee91..2e59675 100644
--- a/src/trace_processor/rpc/httpd.cc
+++ b/src/trace_processor/rpc/httpd.cc
@@ -258,7 +258,7 @@
 void RunHttpRPCServer(std::unique_ptr<TraceProcessor> preloaded_instance,
                       std::string port_number) {
   Httpd srv(std::move(preloaded_instance));
-  base::Optional<int> port_opt = base::StringToInt32(port_number);
+  std::optional<int> port_opt = base::StringToInt32(port_number);
   int port = port_opt.has_value() ? *port_opt : kBindPort;
   srv.Run(port);
 }
diff --git a/src/trace_processor/sorter/trace_token_buffer.cc b/src/trace_processor/sorter/trace_token_buffer.cc
index c8d2899..7541a4d 100644
--- a/src/trace_processor/sorter/trace_token_buffer.cc
+++ b/src/trace_processor/sorter/trace_token_buffer.cc
@@ -22,11 +22,11 @@
 #include <cstring>
 #include <functional>
 #include <limits>
+#include <optional>
 #include <type_traits>
 #include <utility>
 
 #include "perfetto/base/compiler.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/trace_processor/trace_blob.h"
 #include "perfetto/trace_processor/trace_blob_view.h"
 #include "src/trace_processor/importers/common/parser_types.h"
diff --git a/src/trace_processor/sorter/trace_token_buffer.h b/src/trace_processor/sorter/trace_token_buffer.h
index a222bbc..85af6ac 100644
--- a/src/trace_processor/sorter/trace_token_buffer.h
+++ b/src/trace_processor/sorter/trace_token_buffer.h
@@ -19,12 +19,12 @@
 
 #include <cstdint>
 #include <limits>
+#include <optional>
 #include <utility>
 #include <vector>
 
 #include "perfetto/base/compiler.h"
 #include "perfetto/ext/base/circular_queue.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/utils.h"
 #include "perfetto/trace_processor/trace_blob.h"
 #include "perfetto/trace_processor/trace_blob_view.h"
diff --git a/src/trace_processor/sorter/trace_token_buffer_unittest.cc b/src/trace_processor/sorter/trace_token_buffer_unittest.cc
index 7d6292f..a8657d2 100644
--- a/src/trace_processor/sorter/trace_token_buffer_unittest.cc
+++ b/src/trace_processor/sorter/trace_token_buffer_unittest.cc
@@ -16,8 +16,9 @@
 
 #include "src/trace_processor/sorter/trace_token_buffer.h"
 
+#include <optional>
+
 #include "perfetto/base/compiler.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/trace_processor/trace_blob.h"
 #include "perfetto/trace_processor/trace_blob_view.h"
 #include "src/trace_processor/importers/common/parser_types.h"
@@ -145,7 +146,7 @@
   ASSERT_EQ(extracted.trace_packet_data.sequence_state,
             state.current_generation());
   ASSERT_EQ(extracted.thread_instruction_count, 123);
-  ASSERT_EQ(extracted.thread_timestamp, base::nullopt);
+  ASSERT_EQ(extracted.thread_timestamp, std::nullopt);
   ASSERT_DOUBLE_EQ(extracted.counter_value, 0.0);
   ASSERT_EQ(extracted.extra_counter_values, counter_array);
 }
diff --git a/src/trace_processor/sqlite/db_sqlite_table.cc b/src/trace_processor/sqlite/db_sqlite_table.cc
index 552a24b..a97ec8f 100644
--- a/src/trace_processor/sqlite/db_sqlite_table.cc
+++ b/src/trace_processor/sqlite/db_sqlite_table.cc
@@ -28,7 +28,7 @@
 
 namespace {
 
-base::Optional<FilterOp> SqliteOpToFilterOp(int sqlite_op) {
+std::optional<FilterOp> SqliteOpToFilterOp(int sqlite_op) {
   switch (sqlite_op) {
     case SQLITE_INDEX_CONSTRAINT_EQ:
     case SQLITE_INDEX_CONSTRAINT_IS:
@@ -54,7 +54,7 @@
     // TODO(lalitm): start supporting these constraints.
     case SQLITE_INDEX_CONSTRAINT_LIMIT:
     case SQLITE_INDEX_CONSTRAINT_OFFSET:
-      return base::nullopt;
+      return std::nullopt;
     default:
       PERFETTO_FATAL("Currently unsupported constraint");
   }
@@ -221,10 +221,10 @@
 
   const auto& cs = qc.constraints();
   for (uint32_t i = 0; i < cs.size(); ++i) {
-    // SqliteOpToFilterOp will return nullopt for any constraint which we don't
-    // support filtering ourselves. Only omit filtering by SQLite when we can
-    // handle filtering.
-    base::Optional<FilterOp> opt_op = SqliteOpToFilterOp(cs[i].op);
+    // SqliteOpToFilterOp will return std::nullopt for any constraint which we
+    // don't support filtering ourselves. Only omit filtering by SQLite when we
+    // can handle filtering.
+    std::optional<FilterOp> opt_op = SqliteOpToFilterOp(cs[i].op);
     info->sqlite_omit_constraint[i] = opt_op.has_value();
   }
 
@@ -458,7 +458,7 @@
                                   FilterHistory history) {
   // Clear out the iterator before filtering to ensure the destructor is run
   // before the table's destructor.
-  iterator_ = base::nullopt;
+  iterator_ = std::nullopt;
 
   // We reuse this vector to reduce memory allocations on nested subqueries.
   constraints_.resize(qc.constraints().size());
@@ -467,9 +467,9 @@
     const auto& cs = qc.constraints()[i];
     uint32_t col = static_cast<uint32_t>(cs.column);
 
-    // If we get a nullopt FilterOp, that means we should allow SQLite
+    // If we get a std::nullopt FilterOp, that means we should allow SQLite
     // to handle the constraint.
-    base::Optional<FilterOp> opt_op = SqliteOpToFilterOp(cs.op);
+    std::optional<FilterOp> opt_op = SqliteOpToFilterOp(cs.op);
     if (!opt_op)
       continue;
 
@@ -617,9 +617,8 @@
     // TODO(lalitm): investigate some other criteria where it is beneficial
     // to have a fast path and expand to them.
     mode_ = Mode::kSingleRow;
-    single_row_ = filter_map.size() == 1
-                      ? base::make_optional(filter_map.Get(0))
-                      : base::nullopt;
+    single_row_ = filter_map.size() == 1 ? std::make_optional(filter_map.Get(0))
+                                         : std::nullopt;
     eof_ = !single_row_.has_value();
   } else {
     mode_ = Mode::kTable;
diff --git a/src/trace_processor/sqlite/db_sqlite_table.h b/src/trace_processor/sqlite/db_sqlite_table.h
index 4b50e6d..6a2282d 100644
--- a/src/trace_processor/sqlite/db_sqlite_table.h
+++ b/src/trace_processor/sqlite/db_sqlite_table.h
@@ -82,11 +82,11 @@
     std::unique_ptr<Table> dynamic_table_;
 
     // Only valid for Mode::kSingleRow.
-    base::Optional<uint32_t> single_row_;
+    std::optional<uint32_t> single_row_;
 
     // Only valid for Mode::kTable.
-    base::Optional<Table> db_table_;
-    base::Optional<Table::Iterator> iterator_;
+    std::optional<Table> db_table_;
+    std::optional<Table::Iterator> iterator_;
 
     bool eof_ = true;
 
diff --git a/src/trace_processor/sqlite/query_cache.h b/src/trace_processor/sqlite/query_cache.h
index 16a2b6e..f3b8223 100644
--- a/src/trace_processor/sqlite/query_cache.h
+++ b/src/trace_processor/sqlite/query_cache.h
@@ -16,8 +16,7 @@
 
 #ifndef SRC_TRACE_PROCESSOR_SQLITE_QUERY_CACHE_H_
 #define SRC_TRACE_PROCESSOR_SQLITE_QUERY_CACHE_H_
-
-#include "perfetto/ext/base/optional.h"
+#include <optional>
 
 #include "src/trace_processor/db/table.h"
 #include "src/trace_processor/sqlite/query_constraints.h"
diff --git a/src/trace_processor/sqlite/sqlite_raw_table.cc b/src/trace_processor/sqlite/sqlite_raw_table.cc
index f6f0945..feaca00 100644
--- a/src/trace_processor/sqlite/sqlite_raw_table.cc
+++ b/src/trace_processor/sqlite/sqlite_raw_table.cc
@@ -60,7 +60,7 @@
   ArgsSerializer(TraceProcessorContext*,
                  ArgSetId arg_set_id,
                  NullTermStringView event_name,
-                 std::vector<base::Optional<uint32_t>>* field_id_to_arg_index,
+                 std::vector<std::optional<uint32_t>>* field_id_to_arg_index,
                  base::StringWriter*);
 
   void SerializeArgs();
@@ -71,7 +71,7 @@
 
   // Arg writing functions.
   void WriteArgForField(uint32_t field_id, ValueWriter writer) {
-    base::Optional<uint32_t> row = FieldIdToRow(field_id);
+    std::optional<uint32_t> row = FieldIdToRow(field_id);
     if (!row)
       return;
     WriteArgAtRow(*row, writer);
@@ -79,7 +79,7 @@
   void WriteArgForField(uint32_t field_id,
                         base::StringView key,
                         ValueWriter writer) {
-    base::Optional<uint32_t> row = FieldIdToRow(field_id);
+    std::optional<uint32_t> row = FieldIdToRow(field_id);
     if (!row)
       return;
     WriteArg(key, storage_->GetArgValue(*row), writer);
@@ -93,7 +93,7 @@
 
   // Value writing functions.
   void WriteValueForField(uint32_t field_id, ValueWriter writer) {
-    base::Optional<uint32_t> row = FieldIdToRow(field_id);
+    std::optional<uint32_t> row = FieldIdToRow(field_id);
     if (!row)
       return;
     writer(storage_->GetArgValue(*row));
@@ -116,21 +116,21 @@
   }
 
   // Converts a field id to a row in the args table.
-  base::Optional<uint32_t> FieldIdToRow(uint32_t field_id) {
+  std::optional<uint32_t> FieldIdToRow(uint32_t field_id) {
     PERFETTO_DCHECK(field_id > 0);
     PERFETTO_DCHECK(field_id < field_id_to_arg_index_->size());
-    base::Optional<uint32_t> index_in_arg_set =
+    std::optional<uint32_t> index_in_arg_set =
         (*field_id_to_arg_index_)[field_id];
     return index_in_arg_set.has_value()
-               ? base::make_optional(start_row_ + *index_in_arg_set)
-               : base::nullopt;
+               ? std::make_optional(start_row_ + *index_in_arg_set)
+               : std::nullopt;
   }
 
   const TraceStorage* storage_ = nullptr;
   TraceProcessorContext* context_ = nullptr;
   ArgSetId arg_set_id_ = kInvalidArgSetId;
   NullTermStringView event_name_;
-  std::vector<base::Optional<uint32_t>>* field_id_to_arg_index_;
+  std::vector<std::optional<uint32_t>>* field_id_to_arg_index_;
 
   RowMap row_map_;
   uint32_t start_row_ = 0;
@@ -142,7 +142,7 @@
     TraceProcessorContext* context,
     ArgSetId arg_set_id,
     NullTermStringView event_name,
-    std::vector<base::Optional<uint32_t>>* field_id_to_arg_index,
+    std::vector<std::optional<uint32_t>>* field_id_to_arg_index,
     base::StringWriter* writer)
     : context_(context),
       arg_set_id_(arg_set_id),
@@ -205,7 +205,7 @@
     WriteArgForField(SS::kPrevStateFieldNumber, [this](const Variadic& value) {
       PERFETTO_DCHECK(value.type == Variadic::Type::kInt);
       auto state = static_cast<uint16_t>(value.int_value);
-      base::Optional<VersionNumber> kernel_version =
+      std::optional<VersionNumber> kernel_version =
           SystemInfoTracker::GetOrCreate(context_)->GetKernelVersion();
       writer_->AppendString(
           ftrace_utils::TaskState::FromRawPrevState(state, kernel_version)
diff --git a/src/trace_processor/sqlite/sqlite_raw_table.h b/src/trace_processor/sqlite/sqlite_raw_table.h
index a3ec5c2..dcb5c46 100644
--- a/src/trace_processor/sqlite/sqlite_raw_table.h
+++ b/src/trace_processor/sqlite/sqlite_raw_table.h
@@ -38,7 +38,7 @@
 
  private:
   using StringIdMap =
-      base::FlatHashMap<StringId, std::vector<base::Optional<uint32_t>>>;
+      base::FlatHashMap<StringId, std::vector<std::optional<uint32_t>>>;
 
   void SerializePrefix(uint32_t raw_row, base::StringWriter* writer);
 
diff --git a/src/trace_processor/sqlite/sqlite_table.h b/src/trace_processor/sqlite/sqlite_table.h
index 2d9ed50..de94669 100644
--- a/src/trace_processor/sqlite/sqlite_table.h
+++ b/src/trace_processor/sqlite/sqlite_table.h
@@ -22,11 +22,11 @@
 #include <functional>
 #include <limits>
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
 #include "perfetto/base/status.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/utils.h"
 #include "perfetto/trace_processor/basic_types.h"
 #include "src/trace_processor/sqlite/query_constraints.h"
diff --git a/src/trace_processor/sqlite/sqlite_utils.cc b/src/trace_processor/sqlite/sqlite_utils.cc
index 475b1d4..c4077a5 100644
--- a/src/trace_processor/sqlite/sqlite_utils.cc
+++ b/src/trace_processor/sqlite/sqlite_utils.cc
@@ -196,9 +196,9 @@
 
 template <typename T>
 base::Status ExtractFromSqlValueInt(const SqlValue& value,
-                                    base::Optional<T>& out) {
+                                    std::optional<T>& out) {
   if (value.is_null()) {
-    out = base::nullopt;
+    out = std::nullopt;
     return base::OkStatus();
   }
   if (value.type != SqlValue::kLong) {
@@ -221,21 +221,21 @@
 }
 
 base::Status ExtractFromSqlValue(const SqlValue& value,
-                                 base::Optional<int64_t>& out) {
+                                 std::optional<int64_t>& out) {
   return ExtractFromSqlValueInt(value, out);
 }
 base::Status ExtractFromSqlValue(const SqlValue& value,
-                                 base::Optional<int32_t>& out) {
+                                 std::optional<int32_t>& out) {
   return ExtractFromSqlValueInt(value, out);
 }
 base::Status ExtractFromSqlValue(const SqlValue& value,
-                                 base::Optional<uint32_t>& out) {
+                                 std::optional<uint32_t>& out) {
   return ExtractFromSqlValueInt(value, out);
 }
 base::Status ExtractFromSqlValue(const SqlValue& value,
-                                 base::Optional<double>& out) {
+                                 std::optional<double>& out) {
   if (value.is_null()) {
-    out = base::nullopt;
+    out = std::nullopt;
     return base::OkStatus();
   }
   if (value.type != SqlValue::kDouble) {
@@ -248,9 +248,9 @@
   return base::OkStatus();
 }
 base::Status ExtractFromSqlValue(const SqlValue& value,
-                                 base::Optional<const char*>& out) {
+                                 std::optional<const char*>& out) {
   if (value.is_null()) {
-    out = base::nullopt;
+    out = std::nullopt;
     return base::OkStatus();
   }
   if (value.type != SqlValue::kString) {
diff --git a/src/trace_processor/sqlite/sqlite_utils.h b/src/trace_processor/sqlite/sqlite_utils.h
index c3b08bb..ef44c45 100644
--- a/src/trace_processor/sqlite/sqlite_utils.h
+++ b/src/trace_processor/sqlite/sqlite_utils.h
@@ -22,11 +22,11 @@
 #include <bitset>
 #include <cstddef>
 #include <cstring>
+#include <optional>
 #include <utility>
 
 #include "perfetto/base/logging.h"
 #include "perfetto/base/status.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/status_or.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/string_view.h"
@@ -98,7 +98,7 @@
   return sql_value;
 }
 
-inline base::Optional<std::string> SqlValueToString(SqlValue value) {
+inline std::optional<std::string> SqlValueToString(SqlValue value) {
   switch (value.type) {
     case SqlValue::Type::kString:
       return value.AsString();
@@ -108,7 +108,7 @@
       return std::to_string(value.AsLong());
     case SqlValue::Type::kBytes:
     case SqlValue::Type::kNull:
-      return base::nullopt;
+      return std::nullopt;
   }
   PERFETTO_FATAL("For GCC");
 }
@@ -169,7 +169,7 @@
 }
 
 inline base::Status FormatErrorMessage(sqlite3_stmt* stmt,
-                                       base::Optional<base::StringView> sql,
+                                       std::optional<base::StringView> sql,
                                        sqlite3* db,
                                        int error_code) {
   if (stmt) {
@@ -209,7 +209,7 @@
   if (err != SQLITE_DONE) {
     auto db = sqlite3_db_handle(stmt);
     return base::ErrStatus(
-        "%s", FormatErrorMessage(stmt, base::nullopt, db, err).c_message());
+        "%s", FormatErrorMessage(stmt, std::nullopt, db, err).c_message());
   }
   return base::OkStatus();
 }
@@ -228,21 +228,20 @@
 
 // Exracts the given type from the SqlValue if |value| can fit
 // in the provided optional. Note that SqlValue::kNull will always
-// succeed and cause base::nullopt to be set.
+// succeed and cause std::nullopt to be set.
 //
 // Returns base::ErrStatus if the type does not match or does not
 // fit in the width of the provided optional type (i.e. int64 value
 // not fitting in int32 optional).
 base::Status ExtractFromSqlValue(const SqlValue& value,
-                                 base::Optional<int64_t>&);
+                                 std::optional<int64_t>&);
 base::Status ExtractFromSqlValue(const SqlValue& value,
-                                 base::Optional<int32_t>&);
+                                 std::optional<int32_t>&);
 base::Status ExtractFromSqlValue(const SqlValue& value,
-                                 base::Optional<uint32_t>&);
+                                 std::optional<uint32_t>&);
+base::Status ExtractFromSqlValue(const SqlValue& value, std::optional<double>&);
 base::Status ExtractFromSqlValue(const SqlValue& value,
-                                 base::Optional<double>&);
-base::Status ExtractFromSqlValue(const SqlValue& value,
-                                 base::Optional<const char*>&);
+                                 std::optional<const char*>&);
 
 // Returns the column names for the table named by |raw_table_name|.
 base::Status GetColumnsForTable(sqlite3* db,
diff --git a/src/trace_processor/sqlite/sqlite_utils_unittest.cc b/src/trace_processor/sqlite/sqlite_utils_unittest.cc
index b52df66..be28af1 100644
--- a/src/trace_processor/sqlite/sqlite_utils_unittest.cc
+++ b/src/trace_processor/sqlite/sqlite_utils_unittest.cc
@@ -75,7 +75,7 @@
 }
 
 TEST(SqliteUtilsTest, ExtractFromSqlValueInt32) {
-  base::Optional<int32_t> int32;
+  std::optional<int32_t> int32;
 
   static constexpr int64_t kMin = std::numeric_limits<int32_t>::min();
   static constexpr int64_t kMax = std::numeric_limits<int32_t>::max();
@@ -98,7 +98,7 @@
 }
 
 TEST(SqliteUtilsTest, ExtractFromSqlValueUint32) {
-  base::Optional<uint32_t> uint32;
+  std::optional<uint32_t> uint32;
 
   static constexpr int64_t kMin = std::numeric_limits<uint32_t>::min();
   static constexpr int64_t kMax = std::numeric_limits<uint32_t>::max();
@@ -121,7 +121,7 @@
 }
 
 TEST(SqliteUtilsTest, ExtractFromSqlValueInt64) {
-  base::Optional<int64_t> int64;
+  std::optional<int64_t> int64;
 
   static constexpr int64_t kMin = std::numeric_limits<int64_t>::min();
   static constexpr int64_t kMax = std::numeric_limits<int64_t>::max();
@@ -143,7 +143,7 @@
 }
 
 TEST(SqliteUtilsTest, ExtractFromSqlValueDouble) {
-  base::Optional<double> doub;
+  std::optional<double> doub;
 
   static constexpr double kMin = std::numeric_limits<double>::min();
   static constexpr double kMax = std::numeric_limits<double>::max();
@@ -165,7 +165,7 @@
 }
 
 TEST(SqliteUtilsTest, ExtractFromSqlValueString) {
-  base::Optional<const char*> string;
+  std::optional<const char*> string;
 
   ASSERT_TRUE(ExtractFromSqlValue(SqlValue::String("foo"), string).ok());
   ASSERT_STREQ(*string, "foo");
diff --git a/src/trace_processor/storage/trace_storage.h b/src/trace_processor/storage/trace_storage.h
index 5443165..4fe2071 100644
--- a/src/trace_processor/storage/trace_storage.h
+++ b/src/trace_processor/storage/trace_storage.h
@@ -20,6 +20,7 @@
 #include <array>
 #include <deque>
 #include <map>
+#include <optional>
 #include <string>
 #include <unordered_map>
 #include <utility>
@@ -28,7 +29,6 @@
 #include "perfetto/base/logging.h"
 #include "perfetto/base/time.h"
 #include "perfetto/ext/base/hash.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/ext/base/utils.h"
 #include "perfetto/trace_processor/basic_types.h"
@@ -154,13 +154,13 @@
       return thread_instruction_deltas_;
     }
 
-    base::Optional<uint32_t> FindRowForSliceId(SliceId slice_id) const {
+    std::optional<uint32_t> FindRowForSliceId(SliceId slice_id) const {
       auto it =
           std::lower_bound(slice_ids().begin(), slice_ids().end(), slice_id);
       if (it != slice_ids().end() && *it == slice_id) {
         return static_cast<uint32_t>(std::distance(slice_ids().begin(), it));
       }
-      return base::nullopt;
+      return std::nullopt;
     }
 
     void UpdateThreadDeltasForSliceId(SliceId slice_id,
@@ -251,14 +251,14 @@
   }
 
   // Example usage: opt_cpu_failure = GetIndexedStats(stats::cpu_failure, 1);
-  base::Optional<int64_t> GetIndexedStats(size_t key, int index) {
+  std::optional<int64_t> GetIndexedStats(size_t key, int index) {
     PERFETTO_DCHECK(key < stats::kNumKeys);
     PERFETTO_DCHECK(stats::kTypes[key] == stats::kIndexed);
     auto kv = stats_[key].indexed_values.find(index);
     if (kv != stats_[key].indexed_values.end()) {
       return kv->second;
     }
-    return base::nullopt;
+    return std::nullopt;
   }
 
   class ScopedStatsTracer {
@@ -739,12 +739,12 @@
 
   util::Status ExtractArg(uint32_t arg_set_id,
                           const char* key,
-                          base::Optional<Variadic>* result) {
+                          std::optional<Variadic>* result) {
     const auto& args = arg_table();
     RowMap filtered = args.FilterToRowMap(
         {args.arg_set_id().eq(arg_set_id), args.key().eq(key)});
     if (filtered.empty()) {
-      *result = base::nullopt;
+      *result = std::nullopt;
       return util::OkStatus();
     }
     if (filtered.size() > 1) {
@@ -799,11 +799,11 @@
     return variadic_type_ids_[type];
   }
 
-  base::Optional<Variadic::Type> GetVariadicTypeForId(StringId id) const {
+  std::optional<Variadic::Type> GetVariadicTypeForId(StringId id) const {
     auto it =
         std::find(variadic_type_ids_.begin(), variadic_type_ids_.end(), id);
     if (it == variadic_type_ids_.end())
-      return base::nullopt;
+      return std::nullopt;
 
     int64_t idx = std::distance(variadic_type_ids_.begin(), it);
     return static_cast<Variadic::Type>(idx);
@@ -1003,8 +1003,8 @@
 
   result_type operator()(const argument_type& r) const {
     return std::hash<::perfetto::trace_processor::StringId>{}(r.name) ^
-           std::hash<::perfetto::base::Optional<
-               ::perfetto::trace_processor::MappingId>>{}(r.mapping) ^
+           std::hash<std::optional<::perfetto::trace_processor::MappingId>>{}(
+               r.mapping) ^
            std::hash<int64_t>{}(r.rel_pc);
   }
 };
@@ -1018,8 +1018,8 @@
 
   result_type operator()(const argument_type& r) const {
     return std::hash<int64_t>{}(r.depth) ^
-           std::hash<::perfetto::base::Optional<
-               ::perfetto::trace_processor::CallsiteId>>{}(r.parent_id) ^
+           std::hash<std::optional<::perfetto::trace_processor::CallsiteId>>{}(
+               r.parent_id) ^
            std::hash<::perfetto::trace_processor::FrameId>{}(r.frame_id);
   }
 };
diff --git a/src/trace_processor/tables/counter_tables.h b/src/trace_processor/tables/counter_tables.h
index 7df627b..69668b2 100644
--- a/src/trace_processor/tables/counter_tables.h
+++ b/src/trace_processor/tables/counter_tables.h
@@ -32,7 +32,7 @@
   C(int64_t, ts, Column::Flag::kSorted)                \
   C(CounterTrackTable::Id, track_id)                   \
   C(double, value)                                     \
-  C(base::Optional<uint32_t>, arg_set_id)
+  C(std::optional<uint32_t>, arg_set_id)
 
 }  // namespace tables
 }  // namespace trace_processor
diff --git a/src/trace_processor/tables/macros_benchmark.cc b/src/trace_processor/tables/macros_benchmark.cc
index 5df9e8c..c946696 100644
--- a/src/trace_processor/tables/macros_benchmark.cc
+++ b/src/trace_processor/tables/macros_benchmark.cc
@@ -28,7 +28,7 @@
   C(uint32_t, root_sorted, Column::Flag::kSorted)    \
   C(uint32_t, root_non_null)                         \
   C(uint32_t, root_non_null_2)                       \
-  C(base::Optional<uint32_t>, root_nullable)
+  C(std::optional<uint32_t>, root_nullable)
 
 PERFETTO_TP_TABLE(PERFETTO_TP_ROOT_TEST_TABLE);
 
@@ -37,7 +37,7 @@
   PARENT(PERFETTO_TP_ROOT_TEST_TABLE, C)           \
   C(uint32_t, child_sorted, Column::Flag::kSorted) \
   C(uint32_t, child_non_null)                      \
-  C(base::Optional<uint32_t>, child_nullable)
+  C(std::optional<uint32_t>, child_nullable)
 
 PERFETTO_TP_TABLE(PERFETTO_TP_CHILD_TABLE);
 
@@ -264,8 +264,8 @@
     uint32_t value = rnd_engine() % partitions;
 
     RootTestTable::Row row;
-    row.root_nullable = value % 2 == 0 ? perfetto::base::nullopt
-                                       : perfetto::base::make_optional(value);
+    row.root_nullable =
+        value % 2 == 0 ? std::nullopt : std::make_optional(value);
     root.Insert(row);
   }
 
@@ -310,8 +310,8 @@
     uint32_t value = rnd_engine() % partitions;
 
     ChildTestTable::Row row;
-    row.child_nullable = value % 2 == 0 ? perfetto::base::nullopt
-                                        : perfetto::base::make_optional(value);
+    row.child_nullable =
+        value % 2 == 0 ? std::nullopt : std::make_optional(value);
     root.Insert({});
     child.Insert(row);
   }
@@ -488,9 +488,8 @@
     const uint32_t root_value = static_cast<uint32_t>(rnd_engine());
 
     RootTestTable::Row row;
-    row.root_nullable = root_value % 2 == 0
-                            ? perfetto::base::nullopt
-                            : perfetto::base::make_optional(root_value);
+    row.root_nullable =
+        root_value % 2 == 0 ? std::nullopt : std::make_optional(root_value);
     root.Insert(row);
   }
 
@@ -540,17 +539,15 @@
     const uint32_t root_value = static_cast<uint32_t>(rnd_engine());
 
     RootTestTable::Row root_row;
-    root_row.root_nullable = root_value % 2 == 0
-                                 ? perfetto::base::nullopt
-                                 : perfetto::base::make_optional(root_value);
+    root_row.root_nullable =
+        root_value % 2 == 0 ? std::nullopt : std::make_optional(root_value);
     root.Insert(root_row);
 
     const uint32_t child_value = static_cast<uint32_t>(rnd_engine());
 
     ChildTestTable::Row child_row;
-    child_row.root_nullable = child_value % 2 == 0
-                                  ? perfetto::base::nullopt
-                                  : perfetto::base::make_optional(child_value);
+    child_row.root_nullable =
+        child_value % 2 == 0 ? std::nullopt : std::make_optional(child_value);
     child.Insert(child_row);
   }
 
diff --git a/src/trace_processor/tables/macros_internal.h b/src/trace_processor/tables/macros_internal.h
index 4389ce2..059e7b7 100644
--- a/src/trace_processor/tables/macros_internal.h
+++ b/src/trace_processor/tables/macros_internal.h
@@ -576,7 +576,7 @@
                                                                               \
     struct Row : parent_class_name::Row {                                     \
       /*                                                                      \
-       * Expands to Row(col_type1 col1_c, base::Optional<col_type2> col2_c,   \
+       * Expands to Row(col_type1 col1_c, std::optional<col_type2> col2_c,    \
        * ...)                                                                 \
        */                                                                     \
       Row(PERFETTO_TP_ALL_COLUMNS(DEF, PERFETTO_TP_ROW_CONSTRUCTOR)           \
@@ -841,18 +841,18 @@
     }                                                                         \
                                                                               \
     /* Returns a ConstRowReference to the row pointed to by |find_id|. */     \
-    base::Optional<ConstRowReference> FindById(Id find_id) const {            \
-      base::Optional<uint32_t> row = id().IndexOf(find_id);                   \
+    std::optional<ConstRowReference> FindById(Id find_id) const {             \
+      std::optional<uint32_t> row = id().IndexOf(find_id);                    \
       if (!row)                                                               \
-        return base::nullopt;                                                 \
+        return std::nullopt;                                                  \
       return ConstRowReference(this, *row);                                   \
     }                                                                         \
                                                                               \
     /* Returns a RowReference to the row pointed to by |find_id|. */          \
-    base::Optional<RowReference> FindById(Id find_id) {                       \
-      base::Optional<uint32_t> row = id().IndexOf(find_id);                   \
+    std::optional<RowReference> FindById(Id find_id) {                        \
+      std::optional<uint32_t> row = id().IndexOf(find_id);                    \
       if (!row)                                                               \
-        return base::nullopt;                                                 \
+        return std::nullopt;                                                  \
       return RowReference(this, *row);                                        \
     }                                                                         \
                                                                               \
diff --git a/src/trace_processor/tables/macros_unittest.cc b/src/trace_processor/tables/macros_unittest.cc
index 9844a9d..f6414cd 100644
--- a/src/trace_processor/tables/macros_unittest.cc
+++ b/src/trace_processor/tables/macros_unittest.cc
@@ -33,13 +33,13 @@
 #define PERFETTO_TP_TEST_COUNTER_TABLE_DEF(NAME, PARENT, C) \
   NAME(TestCounterTable, "counter")                         \
   PARENT(PERFETTO_TP_TEST_EVENT_TABLE_DEF, C)               \
-  C(base::Optional<double>, value)
+  C(std::optional<double>, value)
 PERFETTO_TP_TABLE(PERFETTO_TP_TEST_COUNTER_TABLE_DEF);
 
 #define PERFETTO_TP_TEST_SLICE_TABLE_DEF(NAME, PARENT, C) \
   NAME(TestSliceTable, "slice")                           \
   PARENT(PERFETTO_TP_TEST_EVENT_TABLE_DEF, C)             \
-  C(base::Optional<int64_t>, dur)                         \
+  C(std::optional<int64_t>, dur)                          \
   C(int64_t, depth)
 PERFETTO_TP_TABLE(PERFETTO_TP_TEST_SLICE_TABLE_DEF);
 
@@ -107,7 +107,7 @@
   ASSERT_EQ(slice_.dur()[0], 10);
   ASSERT_EQ(slice_.depth()[0], 0);
 
-  id = slice_.Insert(TestSliceTable::Row(210, 456, base::nullopt, 0)).id;
+  id = slice_.Insert(TestSliceTable::Row(210, 456, std::nullopt, 0)).id;
   ASSERT_EQ(id.value, 2u);
 
   ASSERT_EQ(event_.type().GetString(2), "slice");
@@ -116,7 +116,7 @@
   ASSERT_EQ(slice_.type().GetString(1), "slice");
   ASSERT_EQ(slice_.ts()[1], 210);
   ASSERT_EQ(slice_.arg_set_id()[1], 456);
-  ASSERT_EQ(slice_.dur()[1], base::nullopt);
+  ASSERT_EQ(slice_.dur()[1], std::nullopt);
   ASSERT_EQ(slice_.depth()[1], 0);
 }
 
diff --git a/src/trace_processor/tables/profiler_tables.h b/src/trace_processor/tables/profiler_tables.h
index fad8cfc..f0e84b2 100644
--- a/src/trace_processor/tables/profiler_tables.h
+++ b/src/trace_processor/tables/profiler_tables.h
@@ -34,7 +34,7 @@
   NAME(StackProfileCallsiteTable, "stack_profile_callsite")     \
   PERFETTO_TP_ROOT_TABLE(PARENT, C)                             \
   C(uint32_t, depth)                                            \
-  C(base::Optional<StackProfileCallsiteTable::Id>, parent_id)   \
+  C(std::optional<StackProfileCallsiteTable::Id>, parent_id)    \
   C(StackProfileFrameTable::Id, frame_id)
 
 }  // namespace tables
diff --git a/src/trace_processor/tables/slice_tables.h b/src/trace_processor/tables/slice_tables.h
index b82e3eb..e33ae19 100644
--- a/src/trace_processor/tables/slice_tables.h
+++ b/src/trace_processor/tables/slice_tables.h
@@ -24,23 +24,23 @@
 namespace trace_processor {
 namespace tables {
 
-#define PERFETTO_TP_SLICE_TABLE_DEF(NAME, PARENT, C)   \
-  NAME(SliceTable, "internal_slice")                   \
-  PERFETTO_TP_ROOT_TABLE(PARENT, C)                    \
-  C(int64_t, ts, Column::Flag::kSorted)                \
-  C(int64_t, dur)                                      \
-  C(TrackTable::Id, track_id)                          \
-  C(base::Optional<StringPool::Id>, category)          \
-  C(base::Optional<StringPool::Id>, name)              \
-  C(uint32_t, depth)                                   \
-  C(int64_t, stack_id)                                 \
-  C(int64_t, parent_stack_id)                          \
-  C(base::Optional<SliceTable::Id>, parent_id)         \
-  C(uint32_t, arg_set_id)                              \
-  C(base::Optional<int64_t>, thread_ts)                \
-  C(base::Optional<int64_t>, thread_dur)               \
-  C(base::Optional<int64_t>, thread_instruction_count) \
-  C(base::Optional<int64_t>, thread_instruction_delta)
+#define PERFETTO_TP_SLICE_TABLE_DEF(NAME, PARENT, C)  \
+  NAME(SliceTable, "internal_slice")                  \
+  PERFETTO_TP_ROOT_TABLE(PARENT, C)                   \
+  C(int64_t, ts, Column::Flag::kSorted)               \
+  C(int64_t, dur)                                     \
+  C(TrackTable::Id, track_id)                         \
+  C(std::optional<StringPool::Id>, category)          \
+  C(std::optional<StringPool::Id>, name)              \
+  C(uint32_t, depth)                                  \
+  C(int64_t, stack_id)                                \
+  C(int64_t, parent_stack_id)                         \
+  C(std::optional<SliceTable::Id>, parent_id)         \
+  C(uint32_t, arg_set_id)                             \
+  C(std::optional<int64_t>, thread_ts)                \
+  C(std::optional<int64_t>, thread_dur)               \
+  C(std::optional<int64_t>, thread_instruction_count) \
+  C(std::optional<int64_t>, thread_instruction_delta)
 
 #define PERFETTO_TP_SCHED_SLICE_TABLE_DEF(NAME, PARENT, C) \
   NAME(SchedSliceTable, "sched_slice")                     \
diff --git a/src/trace_processor/tables/trace_proto_tables.h b/src/trace_processor/tables/trace_proto_tables.h
index 04579ea..aa57b59 100644
--- a/src/trace_processor/tables/trace_proto_tables.h
+++ b/src/trace_processor/tables/trace_proto_tables.h
@@ -27,10 +27,10 @@
 #define PERFETTO_TP_EXPERIMENTAL_PROTO_PATH_TABLE_DEF(NAME, PARENT, C) \
   NAME(ExperimentalProtoPathTable, "experimental_proto_path")          \
   PERFETTO_TP_ROOT_TABLE(PARENT, C)                                    \
-  C(base::Optional<ExperimentalProtoPathTable::Id>, parent_id)         \
+  C(std::optional<ExperimentalProtoPathTable::Id>, parent_id)          \
   C(StringPool::Id, field_type)                                        \
-  C(base::Optional<StringPool::Id>, field_name)                        \
-  C(base::Optional<uint32_t>, arg_set_id)
+  C(std::optional<StringPool::Id>, field_name)                         \
+  C(std::optional<uint32_t>, arg_set_id)
 
 PERFETTO_TP_TABLE(PERFETTO_TP_EXPERIMENTAL_PROTO_PATH_TABLE_DEF);
 
diff --git a/src/trace_processor/trace_database_integrationtest.cc b/src/trace_processor/trace_database_integrationtest.cc
index 720389a..b675c80 100644
--- a/src/trace_processor/trace_database_integrationtest.cc
+++ b/src/trace_processor/trace_database_integrationtest.cc
@@ -16,11 +16,11 @@
 
 #include <algorithm>
 #include <map>
+#include <optional>
 #include <random>
 #include <string>
 
 #include "perfetto/base/logging.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/trace_processor/trace_processor.h"
 #include "protos/perfetto/common/descriptor.pbzero.h"
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 7c38b98..742d7a2 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -1007,7 +1007,7 @@
 }
 
 bool TraceProcessorImpl::IsRootMetricField(const std::string& metric_name) {
-  base::Optional<uint32_t> desc_idx =
+  std::optional<uint32_t> desc_idx =
       pool_.FindDescriptorIdx(".perfetto.protos.TraceMetrics");
   if (!desc_idx.has_value())
     return false;
diff --git a/src/trace_processor/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
index 0a0ba23..1b02b27 100644
--- a/src/trace_processor/trace_processor_shell.cc
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -13,15 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
 #include <sys/stat.h>
-
 #include <cctype>
 #include <cinttypes>
 #include <functional>
 #include <iostream>
+#include <optional>
 #include <string>
 #include <unordered_map>
 #include <unordered_set>
@@ -39,7 +40,6 @@
 #include "perfetto/ext/base/file_utils.h"
 #include "perfetto/ext/base/flat_hash_map.h"
 #include "perfetto/ext/base/getopt.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/string_splitter.h"
 #include "perfetto/ext/base/string_utils.h"
@@ -396,7 +396,7 @@
 
 struct MetricNameAndPath {
   std::string name;
-  base::Optional<std::string> no_ext_path;
+  std::optional<std::string> no_ext_path;
 };
 
 base::Status RunMetrics(const std::vector<MetricNameAndPath>& metrics,
@@ -1345,7 +1345,7 @@
     auto ext_idx = metric_or_path.rfind('.');
     if (ext_idx == std::string::npos) {
       name_and_path.emplace_back(
-          MetricNameAndPath{metric_or_path, base::nullopt});
+          MetricNameAndPath{metric_or_path, std::nullopt});
       continue;
     }
 
@@ -1453,7 +1453,7 @@
           PERFETTO_ELOG("%s", status.c_message());
         }
       } else if (strcmp(command, "width") == 0 && strlen(arg)) {
-        base::Optional<uint32_t> width = base::CStringToUInt32(arg);
+        std::optional<uint32_t> width = base::CStringToUInt32(arg);
         if (!width) {
           PERFETTO_ELOG("Invalid column width specified");
           continue;
diff --git a/src/trace_processor/types/gfp_flags.cc b/src/trace_processor/types/gfp_flags.cc
index acb52fb..a27ab10 100644
--- a/src/trace_processor/types/gfp_flags.cc
+++ b/src/trace_processor/types/gfp_flags.cc
@@ -215,7 +215,7 @@
 }  // namespace
 
 void WriteGfpFlag(uint64_t value,
-                  base::Optional<VersionNumber> version,
+                  std::optional<VersionNumber> version,
                   base::StringWriter* writer) {
   // On all kernel versions if this flag is not set, return GFP_NOWAIT.
   if (value == 0) {
diff --git a/src/trace_processor/types/gfp_flags.h b/src/trace_processor/types/gfp_flags.h
index fb0ae29..04936a8 100644
--- a/src/trace_processor/types/gfp_flags.h
+++ b/src/trace_processor/types/gfp_flags.h
@@ -17,7 +17,8 @@
 #ifndef SRC_TRACE_PROCESSOR_TYPES_GFP_FLAGS_H_
 #define SRC_TRACE_PROCESSOR_TYPES_GFP_FLAGS_H_
 
-#include "perfetto/ext/base/optional.h"
+#include <optional>
+
 #include "perfetto/ext/base/string_writer.h"
 #include "src/trace_processor/types/version_number.h"
 
@@ -28,7 +29,7 @@
 // the kernel version. This function writes a human readable version of the
 // flag.
 void WriteGfpFlag(uint64_t value,
-                  base::Optional<VersionNumber> version,
+                  std::optional<VersionNumber> version,
                   base::StringWriter* writer);
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/types/task_state.cc b/src/trace_processor/types/task_state.cc
index c91a6a4..a13f5b9 100644
--- a/src/trace_processor/types/task_state.cc
+++ b/src/trace_processor/types/task_state.cc
@@ -27,7 +27,7 @@
 // static
 TaskState TaskState::FromRawPrevState(
     uint16_t raw_state,
-    base::Optional<VersionNumber> kernel_version) {
+    std::optional<VersionNumber> kernel_version) {
   return TaskState(raw_state, kernel_version);
 }
 
@@ -55,7 +55,7 @@
 // Note to maintainers: if changing the default kernel assumption or the 4.4
 // codepath, you'll need to update ToRawStateOnlyForSystraceConversions().
 TaskState::TaskState(uint16_t raw_state,
-                     base::Optional<VersionNumber> opt_version) {
+                     std::optional<VersionNumber> opt_version) {
   // Values up to and including 0x20 (EXIT_ZOMBIE) never changed, so map them
   // directly onto ParsedFlag (we use the same flag bits for convenience).
   parsed_ = raw_state & (0x40 - 1);
diff --git a/src/trace_processor/types/task_state.h b/src/trace_processor/types/task_state.h
index 89908dc..0662f72 100644
--- a/src/trace_processor/types/task_state.h
+++ b/src/trace_processor/types/task_state.h
@@ -19,8 +19,8 @@
 
 #include <stdint.h>
 #include <array>
+#include <optional>
 
-#include "perfetto/ext/base/optional.h"
 #include "src/trace_processor/types/version_number.h"
 
 namespace perfetto {
@@ -87,7 +87,7 @@
 
   static TaskState FromRawPrevState(
       uint16_t raw_state,
-      base::Optional<VersionNumber> kernel_version);
+      std::optional<VersionNumber> kernel_version);
   static TaskState FromSystrace(const char* state_str);
   static TaskState FromParsedFlags(uint16_t parsed_state);
 
@@ -115,7 +115,7 @@
  private:
   TaskState() = default;
   explicit TaskState(uint16_t raw_state,
-                     base::Optional<VersionNumber> kernel_version);
+                     std::optional<VersionNumber> kernel_version);
   explicit TaskState(const char* state_str);
 
   bool is_runnable() const { return !(parsed_ & ~kPreempted); }
diff --git a/src/trace_processor/types/task_state_unittests.cc b/src/trace_processor/types/task_state_unittests.cc
index decc199..e08fea2 100644
--- a/src/trace_processor/types/task_state_unittests.cc
+++ b/src/trace_processor/types/task_state_unittests.cc
@@ -25,7 +25,7 @@
 
 TEST(TaskStateUnittest, PrevStateDefaultsToKernelVersion4p4) {
   auto from_raw = [](uint16_t raw) {
-    return TaskState::FromRawPrevState(raw, base::nullopt);
+    return TaskState::FromRawPrevState(raw, std::nullopt);
   };
 
   // No kernel version -> default to 4.4
@@ -95,7 +95,7 @@
 TEST(TaskStateUnittest, PreemptedFlag) {
   // Historical TASK_STATE_MAX as of 4.4:
   {
-    TaskState state = TaskState::FromRawPrevState(0x0800, base::nullopt);
+    TaskState state = TaskState::FromRawPrevState(0x0800, std::nullopt);
     EXPECT_STREQ(state.ToString().data(), "R+");
   }
   // TASK_STATE_MAX moved due to TASK_NEW:
@@ -139,7 +139,7 @@
   auto roundtrip = [](const char* in) {
     uint16_t raw =
         TaskState::FromSystrace(in).ToRawStateOnlyForSystraceConversions();
-    return TaskState::FromRawPrevState(raw, base::nullopt).ToString('|');
+    return TaskState::FromRawPrevState(raw, std::nullopt).ToString('|');
   };
 
   EXPECT_STREQ(roundtrip("R").data(), "R");
diff --git a/src/trace_processor/util/annotated_callsites.cc b/src/trace_processor/util/annotated_callsites.cc
index 840577d..b53e3cf 100644
--- a/src/trace_processor/util/annotated_callsites.cc
+++ b/src/trace_processor/util/annotated_callsites.cc
@@ -17,8 +17,8 @@
 #include "src/trace_processor/util/annotated_callsites.h"
 
 #include <iostream>
+#include <optional>
 
-#include "perfetto/ext/base/optional.h"
 #include "src/trace_processor/tables/profiler_tables_py.h"
 #include "src/trace_processor/types/trace_processor_context.h"
 
@@ -30,13 +30,13 @@
       // String to identify trampoline frames. If the string does not exist in
       // TraceProcessor's StringPool (nullopt) then there will be no trampoline
       // frames in the trace so there is no point in adding it to the pool to do
-      // all comparisons, instead we initialize the member to nullopt and the
-      // string comparisons will all fail.
+      // all comparisons, instead we initialize the member to std::nullopt and
+      // the string comparisons will all fail.
       art_jni_trampoline_(
           context->storage->string_pool().GetId("art_jni_trampoline")) {}
 
 AnnotatedCallsites::State AnnotatedCallsites::GetState(
-    base::Optional<CallsiteId> id) {
+    std::optional<CallsiteId> id) {
   if (!id) {
     return State::kInitial;
   }
@@ -75,8 +75,8 @@
   // only the libart frames does not clean up all of the JNI-related frames.
   auto frame = *context_.storage->stack_profile_frame_table().FindById(
       callsite.frame_id());
-  // art_jni_trampoline_ could be nullopt if the string does not exist in the
-  // StringPool, but that also means no frame will ever have that name.
+  // art_jni_trampoline_ could be std::nullopt if the string does not exist in
+  // the StringPool, but that also means no frame will ever have that name.
   if (art_jni_trampoline_.has_value() &&
       frame.name() == art_jni_trampoline_.value()) {
     return {State::kKeepNext, CallsiteAnnotation::kCommonFrame};
diff --git a/src/trace_processor/util/annotated_callsites.h b/src/trace_processor/util/annotated_callsites.h
index 0fbb555..e68683c 100644
--- a/src/trace_processor/util/annotated_callsites.h
+++ b/src/trace_processor/util/annotated_callsites.h
@@ -17,8 +17,9 @@
 #ifndef SRC_TRACE_PROCESSOR_UTIL_ANNOTATED_CALLSITES_H_
 #define SRC_TRACE_PROCESSOR_UTIL_ANNOTATED_CALLSITES_H_
 
+#include <optional>
+
 #include <unordered_map>
-#include "perfetto/ext/base/optional.h"
 #include "src/trace_processor/containers/string_pool.h"
 #include "src/trace_processor/storage/trace_storage.h"
 
@@ -71,7 +72,7 @@
   // mode, based on the mapping.
   enum class State { kInitial, kEraseLibart, kKeepNext };
 
-  State GetState(base::Optional<CallsiteId> id);
+  State GetState(std::optional<CallsiteId> id);
 
   std::pair<State, CallsiteAnnotation> Get(
       const tables::StackProfileCallsiteTable::ConstRowReference& callsite);
@@ -80,7 +81,7 @@
   MapType ClassifyMap(NullTermStringView map);
 
   const TraceProcessorContext& context_;
-  const base::Optional<StringPool::Id> art_jni_trampoline_;
+  const std::optional<StringPool::Id> art_jni_trampoline_;
 
   std::unordered_map<MappingId, MapType> map_types_;
   std::unordered_map<CallsiteId, State> states_;
diff --git a/src/trace_processor/util/bump_allocator.cc b/src/trace_processor/util/bump_allocator.cc
index 1469525..48cf16d 100644
--- a/src/trace_processor/util/bump_allocator.cc
+++ b/src/trace_processor/util/bump_allocator.cc
@@ -15,11 +15,12 @@
  */
 
 #include "src/trace_processor/util/bump_allocator.h"
+
 #include <limits>
+#include <optional>
 
 #include "perfetto/base/compiler.h"
 #include "perfetto/base/logging.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/utils.h"
 
 namespace perfetto {
@@ -55,7 +56,7 @@
 
   // Fast path: check if we have space to service this allocation in the current
   // chunk.
-  base::Optional<AllocId> alloc_id = TryAllocInLastChunk(size);
+  std::optional<AllocId> alloc_id = TryAllocInLastChunk(size);
   if (alloc_id) {
     return *alloc_id;
   }
@@ -113,10 +114,10 @@
   return AllocId{LastChunkIndex(), chunks_.back().bump_offset};
 }
 
-base::Optional<BumpAllocator::AllocId> BumpAllocator::TryAllocInLastChunk(
+std::optional<BumpAllocator::AllocId> BumpAllocator::TryAllocInLastChunk(
     uint32_t size) {
   if (chunks_.empty()) {
-    return base::nullopt;
+    return std::nullopt;
   }
 
   // TODO(266983484): consider switching this to bump downwards instead of
@@ -134,7 +135,7 @@
   uint32_t alloc_offset = chunk.bump_offset;
   uint32_t new_bump_offset = chunk.bump_offset + size;
   if (new_bump_offset > kChunkSize) {
-    return base::nullopt;
+    return std::nullopt;
   }
 
   // Set the new offset equal to the end of this allocation and increment the
diff --git a/src/trace_processor/util/bump_allocator.h b/src/trace_processor/util/bump_allocator.h
index b7b3499..985b5bc 100644
--- a/src/trace_processor/util/bump_allocator.h
+++ b/src/trace_processor/util/bump_allocator.h
@@ -22,9 +22,10 @@
 #include <cstring>
 #include <limits>
 #include <memory>
+#include <optional>
 #include <tuple>
+
 #include "perfetto/ext/base/circular_queue.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/utils.h"
 
 namespace perfetto {
@@ -165,8 +166,8 @@
   };
 
   // Tries to allocate |size| bytes in the final chunk in |chunks_|. Returns
-  // an AllocId if this was successful or base::nullopt otherwise.
-  base::Optional<AllocId> TryAllocInLastChunk(uint32_t size);
+  // an AllocId if this was successful or std::nullopt otherwise.
+  std::optional<AllocId> TryAllocInLastChunk(uint32_t size);
 
   uint64_t ChunkIndexToQueueIndex(uint64_t chunk_index) const {
     return chunk_index - erased_front_chunks_count_;
diff --git a/src/trace_processor/util/descriptors.cc b/src/trace_processor/util/descriptors.cc
index 16cbd66..98029da 100644
--- a/src/trace_processor/util/descriptors.cc
+++ b/src/trace_processor/util/descriptors.cc
@@ -46,7 +46,7 @@
       f_decoder.label() == FieldDescriptorProto::LABEL_REPEATED, is_extension);
 }
 
-base::Optional<uint32_t> DescriptorPool::ResolveShortType(
+std::optional<uint32_t> DescriptorPool::ResolveShortType(
     const std::string& parent_path,
     const std::string& short_type) {
   PERFETTO_DCHECK(!short_type.empty());
@@ -59,7 +59,7 @@
     return opt_idx;
 
   if (parent_path.empty())
-    return base::nullopt;
+    return std::nullopt;
 
   auto parent_dot_idx = parent_path.rfind('.');
   auto parent_substr = parent_dot_idx == std::string::npos
@@ -95,7 +95,7 @@
 util::Status DescriptorPool::AddNestedProtoDescriptors(
     const std::string& file_name,
     const std::string& package_name,
-    base::Optional<uint32_t> parent_idx,
+    std::optional<uint32_t> parent_idx,
     protozero::ConstBytes descriptor_proto,
     std::vector<ExtensionInfo>* extensions,
     bool merge_existing_messages) {
@@ -166,7 +166,7 @@
 util::Status DescriptorPool::AddEnumProtoDescriptors(
     const std::string& file_name,
     const std::string& package_name,
-    base::Optional<uint32_t> parent_idx,
+    std::optional<uint32_t> parent_idx,
     protozero::ConstBytes descriptor_proto,
     bool merge_existing_messages) {
   protos::pbzero::EnumDescriptorProto::Decoder decoder(descriptor_proto);
@@ -186,7 +186,7 @@
   if (!prev_idx.has_value()) {
     ProtoDescriptor proto_descriptor(file_name, package_name, full_name,
                                      ProtoDescriptor::Type::kEnum,
-                                     base::nullopt);
+                                     std::nullopt);
     prev_idx = AddProtoDescriptor(std::move(proto_descriptor));
   }
   ProtoDescriptor& proto_descriptor = descriptors_[*prev_idx];
@@ -226,13 +226,12 @@
     std::string package = "." + base::StringView(file.package()).ToStdString();
     for (auto message_it = file.message_type(); message_it; ++message_it) {
       RETURN_IF_ERROR(AddNestedProtoDescriptors(
-          file_name, package, base::nullopt, *message_it, &extensions,
+          file_name, package, std::nullopt, *message_it, &extensions,
           merge_existing_messages));
     }
     for (auto enum_it = file.enum_type(); enum_it; ++enum_it) {
-      RETURN_IF_ERROR(AddEnumProtoDescriptors(file_name, package, base::nullopt,
-                                              *enum_it,
-                                              merge_existing_messages));
+      RETURN_IF_ERROR(AddEnumProtoDescriptors(
+          file_name, package, std::nullopt, *enum_it, merge_existing_messages));
     }
     for (auto ext_it = file.extension(); ext_it; ++ext_it) {
       extensions.emplace_back(package, *ext_it);
@@ -271,11 +270,11 @@
   return util::OkStatus();
 }
 
-base::Optional<uint32_t> DescriptorPool::FindDescriptorIdx(
+std::optional<uint32_t> DescriptorPool::FindDescriptorIdx(
     const std::string& full_name) const {
   auto it = full_name_to_descriptor_index_.find(full_name);
   if (it == full_name_to_descriptor_index_.end()) {
-    return base::nullopt;
+    return std::nullopt;
   }
   return it->second;
 }
@@ -317,7 +316,7 @@
                                  std::string package_name,
                                  std::string full_name,
                                  Type type,
-                                 base::Optional<uint32_t> parent_id)
+                                 std::optional<uint32_t> parent_id)
     : file_name_(std::move(file_name)),
       package_name_(std::move(package_name)),
       full_name_(std::move(full_name)),
diff --git a/src/trace_processor/util/descriptors.h b/src/trace_processor/util/descriptors.h
index 71dd5cc..33dbcf0 100644
--- a/src/trace_processor/util/descriptors.h
+++ b/src/trace_processor/util/descriptors.h
@@ -18,13 +18,13 @@
 #define SRC_TRACE_PROCESSOR_UTIL_DESCRIPTORS_H_
 
 #include <algorithm>
+#include <optional>
 #include <set>
 #include <string>
 #include <unordered_map>
 #include <vector>
 
 #include "perfetto/base/status.h"
-#include "perfetto/ext/base/optional.h"
 #include "protos/perfetto/common/descriptor.pbzero.h"
 
 namespace protozero {
@@ -77,7 +77,7 @@
                   std::string package_name,
                   std::string full_name,
                   Type type,
-                  base::Optional<uint32_t> parent_id);
+                  std::optional<uint32_t> parent_id);
 
   void AddField(FieldDescriptor descriptor) {
     PERFETTO_DCHECK(type_ == Type::kMessage);
@@ -114,18 +114,18 @@
     return &it->second;
   }
 
-  base::Optional<std::string> FindEnumString(const int32_t value) const {
+  std::optional<std::string> FindEnumString(const int32_t value) const {
     PERFETTO_DCHECK(type_ == Type::kEnum);
     auto it = enum_names_by_value_.find(value);
-    return it == enum_names_by_value_.end() ? base::nullopt
-                                            : base::make_optional(it->second);
+    return it == enum_names_by_value_.end() ? std::nullopt
+                                            : std::make_optional(it->second);
   }
 
-  base::Optional<int32_t> FindEnumValue(const std::string& value) const {
+  std::optional<int32_t> FindEnumValue(const std::string& value) const {
     PERFETTO_DCHECK(type_ == Type::kEnum);
     auto it = enum_values_by_name_.find(value);
-    return it == enum_values_by_name_.end() ? base::nullopt
-                                            : base::make_optional(it->second);
+    return it == enum_values_by_name_.end() ? std::nullopt
+                                            : std::make_optional(it->second);
   }
 
   const std::string& file_name() const { return file_name_; }
@@ -148,7 +148,7 @@
   std::string package_name_;
   std::string full_name_;
   const Type type_;
-  base::Optional<uint32_t> parent_id_;
+  std::optional<uint32_t> parent_id_;
   std::unordered_map<uint32_t, FieldDescriptor> fields_;
   std::unordered_map<int32_t, std::string> enum_names_by_value_;
   std::unordered_map<std::string, int32_t> enum_values_by_name_;
@@ -166,8 +166,7 @@
       const std::vector<std::string>& skip_prefixes = {},
       bool merge_existing_messages = false);
 
-  base::Optional<uint32_t> FindDescriptorIdx(
-      const std::string& full_name) const;
+  std::optional<uint32_t> FindDescriptorIdx(const std::string& full_name) const;
 
   std::vector<uint8_t> SerializeAsDescriptorSet();
 
@@ -182,13 +181,13 @@
  private:
   base::Status AddNestedProtoDescriptors(const std::string& file_name,
                                          const std::string& package_name,
-                                         base::Optional<uint32_t> parent_idx,
+                                         std::optional<uint32_t> parent_idx,
                                          protozero::ConstBytes descriptor_proto,
                                          std::vector<ExtensionInfo>* extensions,
                                          bool merge_existing_messages);
   base::Status AddEnumProtoDescriptors(const std::string& file_name,
                                        const std::string& package_name,
-                                       base::Optional<uint32_t> parent_idx,
+                                       std::optional<uint32_t> parent_idx,
                                        protozero::ConstBytes descriptor_proto,
                                        bool merge_existing_messages);
 
@@ -197,8 +196,8 @@
 
   // Recursively searches for the given short type in all parent messages
   // and packages.
-  base::Optional<uint32_t> ResolveShortType(const std::string& parent_path,
-                                            const std::string& short_type);
+  std::optional<uint32_t> ResolveShortType(const std::string& parent_path,
+                                           const std::string& short_type);
 
   // Adds a new descriptor to the pool and returns its index. There must not be
   // already a descriptor with the same full_name in the pool.
diff --git a/src/trace_processor/util/glob.h b/src/trace_processor/util/glob.h
index 4a413bb..ab9eec9 100644
--- a/src/trace_processor/util/glob.h
+++ b/src/trace_processor/util/glob.h
@@ -17,9 +17,9 @@
 #ifndef SRC_TRACE_PROCESSOR_UTIL_GLOB_H_
 #define SRC_TRACE_PROCESSOR_UTIL_GLOB_H_
 
+#include <optional>
 #include <vector>
 
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/small_vector.h"
 #include "perfetto/ext/base/string_splitter.h"
 #include "perfetto/ext/base/string_view.h"
diff --git a/src/trace_processor/util/profile_builder.cc b/src/trace_processor/util/profile_builder.cc
index bb99c71..8856825 100644
--- a/src/trace_processor/util/profile_builder.cc
+++ b/src/trace_processor/util/profile_builder.cc
@@ -20,10 +20,10 @@
 #include <deque>
 #include <iostream>
 #include <iterator>
+#include <optional>
 #include <vector>
 
 #include "perfetto/base/logging.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/ext/trace_processor/demangle.h"
@@ -319,7 +319,7 @@
 
   const auto& cs_table = context_.storage->stack_profile_callsite_table();
 
-  base::Optional<tables::StackProfileCallsiteTable::ConstRowReference>
+  std::optional<tables::StackProfileCallsiteTable::ConstRowReference>
       start_ref = cs_table.FindById(callsite_id);
   if (!start_ref) {
     return location_ids;
@@ -329,7 +329,7 @@
       start_ref->frame_id(), annotated ? annotations_.GetAnnotation(*start_ref)
                                        : CallsiteAnnotation::kNone));
 
-  base::Optional<CallsiteId> parent_id = start_ref->parent_id();
+  std::optional<CallsiteId> parent_id = start_ref->parent_id();
   while (parent_id) {
     auto parent_ref = cs_table.FindById(*parent_id);
     location_ids.Append(WriteLocationIfNeeded(
@@ -428,7 +428,7 @@
 }
 
 std::vector<GProfileBuilder::Line> GProfileBuilder::GetLinesForSymbolSetId(
-    base::Optional<uint32_t> symbol_set_id,
+    std::optional<uint32_t> symbol_set_id,
     CallsiteAnnotation annotation,
     uint64_t mapping_id) {
   if (!symbol_set_id) {
@@ -601,7 +601,7 @@
 void GProfileBuilder::WriteMappings() {
   // The convention in pprof files is to write the mapping for the main binary
   // first. So lets do just that.
-  base::Optional<uint64_t> main_mapping_id = GuessMainBinary();
+  std::optional<uint64_t> main_mapping_id = GuessMainBinary();
   if (main_mapping_id) {
     WriteMapping(*main_mapping_id);
   }
@@ -615,7 +615,7 @@
   }
 }
 
-base::Optional<uint64_t> GProfileBuilder::GuessMainBinary() const {
+std::optional<uint64_t> GProfileBuilder::GuessMainBinary() const {
   std::vector<int64_t> mapping_scores;
 
   for (const auto& mapping : mappings_) {
@@ -625,7 +625,7 @@
   auto it = std::max_element(mapping_scores.begin(), mapping_scores.end());
 
   if (it == mapping_scores.end()) {
-    return base::nullopt;
+    return std::nullopt;
   }
 
   return static_cast<uint64_t>(std::distance(mapping_scores.begin(), it) + 1);
diff --git a/src/trace_processor/util/profile_builder.h b/src/trace_processor/util/profile_builder.h
index 8e24cb9..cfd5406 100644
--- a/src/trace_processor/util/profile_builder.h
+++ b/src/trace_processor/util/profile_builder.h
@@ -17,7 +17,8 @@
 #ifndef SRC_TRACE_PROCESSOR_UTIL_PROFILE_BUILDER_H_
 #define SRC_TRACE_PROCESSOR_UTIL_PROFILE_BUILDER_H_
 
-#include "perfetto/ext/base/optional.h"
+#include <optional>
+
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/protozero/packed_repeated_fields.h"
 #include "perfetto/protozero/scattered_heap_buffer.h"
@@ -257,7 +258,7 @@
       bool annotated);
 
   std::vector<Line> GetLinesForSymbolSetId(
-      base::Optional<uint32_t> symbol_set_id,
+      std::optional<uint32_t> symbol_set_id,
       CallsiteAnnotation annotation,
       uint64_t mapping_id);
 
@@ -306,7 +307,7 @@
 
   // Goes over the list of staged mappings and tries to determine which is the
   // most likely main binary.
-  base::Optional<uint64_t> GuessMainBinary() const;
+  std::optional<uint64_t> GuessMainBinary() const;
 
   bool AddSample(const protozero::PackedVarInt& location_ids,
                  const protozero::PackedVarInt& values);
diff --git a/src/trace_processor/util/proto_profiler.cc b/src/trace_processor/util/proto_profiler.cc
index 1bcd3ff..d093de2 100644
--- a/src/trace_processor/util/proto_profiler.cc
+++ b/src/trace_processor/util/proto_profiler.cc
@@ -91,8 +91,8 @@
   field_path_.emplace_back(0, nullptr, root_message_idx_, descriptor);
 }
 
-base::Optional<size_t> SizeProfileComputer::GetNext() {
-  base::Optional<size_t> result;
+std::optional<size_t> SizeProfileComputer::GetNext() {
+  std::optional<size_t> result;
   if (state_stack_.empty())
     return result;
 
diff --git a/src/trace_processor/util/proto_profiler.h b/src/trace_processor/util/proto_profiler.h
index ebc3670..a5375de 100644
--- a/src/trace_processor/util/proto_profiler.h
+++ b/src/trace_processor/util/proto_profiler.h
@@ -78,9 +78,9 @@
   // TODO(kraskevich): consider switching to internal DescriptorPool.
   void Reset(const uint8_t* ptr, size_t size);
 
-  // Returns the next sample size, or nullopt if data is exhausted. The
+  // Returns the next sample size, or std::nullopt if data is exhausted. The
   // associated path can be queried with GetPath().
-  base::Optional<size_t> GetNext();
+  std::optional<size_t> GetNext();
 
   // Returns the field path associated with the last sample returned by
   // GetNext().
diff --git a/src/trace_processor/util/proto_to_args_parser.cc b/src/trace_processor/util/proto_to_args_parser.cc
index b462d3a..61a8d78 100644
--- a/src/trace_processor/util/proto_to_args_parser.cc
+++ b/src/trace_processor/util/proto_to_args_parser.cc
@@ -50,8 +50,8 @@
     : key_(other.key_),
       old_flat_key_length_(other.old_flat_key_length_),
       old_key_length_(other.old_key_length_) {
-  other.old_flat_key_length_ = base::nullopt;
-  other.old_key_length_ = base::nullopt;
+  other.old_flat_key_length_ = std::nullopt;
+  other.old_key_length_ = std::nullopt;
 }
 
 ProtoToArgsParser::ScopedNestedKeyContext::~ScopedNestedKeyContext() {
@@ -63,8 +63,8 @@
     key_.flat_key.resize(old_flat_key_length_.value());
   if (old_key_length_)
     key_.key.resize(old_key_length_.value());
-  old_flat_key_length_ = base::nullopt;
-  old_key_length_ = base::nullopt;
+  old_flat_key_length_ = std::nullopt;
+  old_key_length_ = std::nullopt;
 }
 
 ProtoToArgsParser::Delegate::~Delegate() = default;
@@ -171,7 +171,7 @@
 
   // If we have an override parser then use that instead and move onto the
   // next loop.
-  if (base::Optional<base::Status> status =
+  if (std::optional<base::Status> status =
           MaybeApplyOverrideForField(field, delegate)) {
     return *status;
   }
@@ -200,23 +200,23 @@
   type_overrides_[type] = std::move(func);
 }
 
-base::Optional<base::Status> ProtoToArgsParser::MaybeApplyOverrideForField(
+std::optional<base::Status> ProtoToArgsParser::MaybeApplyOverrideForField(
     const protozero::Field& field,
     Delegate& delegate) {
   auto it = field_overrides_.find(key_prefix_.flat_key);
   if (it == field_overrides_.end())
-    return base::nullopt;
+    return std::nullopt;
   return it->second(field, delegate);
 }
 
-base::Optional<base::Status> ProtoToArgsParser::MaybeApplyOverrideForType(
+std::optional<base::Status> ProtoToArgsParser::MaybeApplyOverrideForType(
     const std::string& message_type,
     ScopedNestedKeyContext& key,
     const protozero::ConstBytes& data,
     Delegate& delegate) {
   auto it = type_overrides_.find(message_type);
   if (it == type_overrides_.end())
-    return base::nullopt;
+    return std::nullopt;
   return it->second(key, data, delegate);
 }
 
diff --git a/src/trace_processor/util/proto_to_args_parser.h b/src/trace_processor/util/proto_to_args_parser.h
index 7252a0f..be359cd 100644
--- a/src/trace_processor/util/proto_to_args_parser.h
+++ b/src/trace_processor/util/proto_to_args_parser.h
@@ -17,6 +17,8 @@
 #ifndef SRC_TRACE_PROCESSOR_UTIL_PROTO_TO_ARGS_PARSER_H_
 #define SRC_TRACE_PROCESSOR_UTIL_PROTO_TO_ARGS_PARSER_H_
 
+#include <functional>
+
 #include "perfetto/base/status.h"
 #include "perfetto/protozero/field.h"
 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
@@ -168,8 +170,8 @@
     struct ScopedStringAppender;
 
     Key& key_;
-    base::Optional<size_t> old_flat_key_length_ = base::nullopt;
-    base::Optional<size_t> old_key_length_ = base::nullopt;
+    std::optional<size_t> old_flat_key_length_ = std::nullopt;
+    std::optional<size_t> old_key_length_ = std::nullopt;
   };
 
   // These methods can be called from parsing overrides to enter nested
@@ -179,14 +181,14 @@
   ScopedNestedKeyContext EnterArray(size_t index);
 
   using ParsingOverrideForField =
-      std::function<base::Optional<base::Status>(const protozero::Field&,
-                                                 Delegate& delegate)>;
+      std::function<std::optional<base::Status>(const protozero::Field&,
+                                                Delegate& delegate)>;
 
   // Installs an override for the field at the specified path. We will invoke
   // |parsing_override| when the field is encountered.
   //
   // The return value of |parsing_override| indicates whether the override
-  // parsed the sub-message and ProtoToArgsParser should skip it (base::nullopt)
+  // parsed the sub-message and ProtoToArgsParser should skip it (std::nullopt)
   // or the sub-message should continue to be parsed by ProtoToArgsParser using
   // the descriptor (base::Status).
   //
@@ -207,7 +209,7 @@
   void AddParsingOverrideForField(const std::string& field_path,
                                   ParsingOverrideForField parsing_override);
 
-  using ParsingOverrideForType = std::function<base::Optional<base::Status>(
+  using ParsingOverrideForType = std::function<std::optional<base::Status>(
       ScopedNestedKeyContext& key,
       const protozero::ConstBytes& data,
       Delegate& delegate)>;
@@ -217,7 +219,7 @@
   // Note that the path-based overrides take precedence over type overrides.
   //
   // The return value of |parsing_override| indicates whether the override
-  // parsed the sub-message and ProtoToArgsParser should skip it (base::nullopt)
+  // parsed the sub-message and ProtoToArgsParser should skip it (std::nullopt)
   // or the sub-message should continue to be parsed by ProtoToArgsParser using
   // the descriptor (base::Status).
   //
@@ -247,11 +249,11 @@
                           Delegate& delegate,
                           int* unknown_extensions);
 
-  base::Optional<base::Status> MaybeApplyOverrideForField(
+  std::optional<base::Status> MaybeApplyOverrideForField(
       const protozero::Field&,
       Delegate& delegate);
 
-  base::Optional<base::Status> MaybeApplyOverrideForType(
+  std::optional<base::Status> MaybeApplyOverrideForType(
       const std::string& message_type,
       ScopedNestedKeyContext& key,
       const protozero::ConstBytes& data,
diff --git a/src/trace_processor/util/proto_to_args_parser_unittest.cc b/src/trace_processor/util/proto_to_args_parser_unittest.cc
index 22291ab..16120fc 100644
--- a/src/trace_processor/util/proto_to_args_parser_unittest.cc
+++ b/src/trace_processor/util/proto_to_args_parser_unittest.cc
@@ -311,7 +311,7 @@
         ++val;
         EXPECT_EQ(1, val);
         EXPECT_EQ(field.type(), protozero::proto_utils::ProtoWireType::kVarInt);
-        return base::nullopt;
+        return std::nullopt;
       });
 
   status = parser.ParseMessage(
@@ -358,13 +358,13 @@
   // multiple args rows.
   parser.AddParsingOverrideForField(
       "super_nested.value_c",
-      [](const protozero::Field& field, ProtoToArgsParser::Delegate& delegate)
-          -> base::Optional<base::Status> {
+      [](const protozero::Field& field,
+         ProtoToArgsParser::Delegate& delegate) -> std::optional<base::Status> {
         auto* decoder = delegate.GetInternedMessage(
             protos::pbzero::InternedData::kSourceLocations, field.as_uint64());
         if (!decoder) {
           // Lookup failed fall back on default behaviour.
-          return base::nullopt;
+          return std::nullopt;
         }
         delegate.AddString(ProtoToArgsParser::Key("file_name"),
                            protozero::ConstChars{"file", 4});
diff --git a/src/trace_processor/util/protozero_to_text.cc b/src/trace_processor/util/protozero_to_text.cc
index 90897ad..a7f9fcf 100644
--- a/src/trace_processor/util/protozero_to_text.cc
+++ b/src/trace_processor/util/protozero_to_text.cc
@@ -15,8 +15,8 @@
  */
 
 #include "src/trace_processor/util/protozero_to_text.h"
+#include <optional>
 
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/protozero/proto_decoder.h"
@@ -389,7 +389,7 @@
                              const DescriptorPool& pool,
                              std::string* indents,
                              std::string* output) {
-  base::Optional<uint32_t> opt_proto_desc_idx = pool.FindDescriptorIdx(type);
+  std::optional<uint32_t> opt_proto_desc_idx = pool.FindDescriptorIdx(type);
   const ProtoDescriptor* opt_proto_descriptor =
       opt_proto_desc_idx ? &pool.descriptors()[*opt_proto_desc_idx] : nullptr;
   const bool include_new_lines = new_lines_mode == kIncludeNewLines;
diff --git a/src/trace_processor/util/sql_argument.cc b/src/trace_processor/util/sql_argument.cc
index 3fc0b48..11cd010 100644
--- a/src/trace_processor/util/sql_argument.cc
+++ b/src/trace_processor/util/sql_argument.cc
@@ -27,7 +27,7 @@
   return std::find_if(str.begin(), str.end(), pred) == str.end();
 }
 
-base::Optional<Type> ParseType(base::StringView str) {
+std::optional<Type> ParseType(base::StringView str) {
   if (str == "BOOL") {
     return Type::kBool;
   } else if (str == "INT") {
@@ -47,7 +47,7 @@
   } else if (str == "BYTES") {
     return Type::kBytes;
   }
-  return base::nullopt;
+  return std::nullopt;
 }
 
 const char* TypeToHumanFriendlyString(sql_argument::Type type) {
diff --git a/src/trace_processor/util/sql_argument.h b/src/trace_processor/util/sql_argument.h
index 7b7b9b3..8876bed 100644
--- a/src/trace_processor/util/sql_argument.h
+++ b/src/trace_processor/util/sql_argument.h
@@ -16,9 +16,9 @@
 
 #ifndef SRC_TRACE_PROCESSOR_UTIL_SQL_ARGUMENT_H_
 #define SRC_TRACE_PROCESSOR_UTIL_SQL_ARGUMENT_H_
+#include <optional>
 
 #include "perfetto/base/status.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/trace_processor/basic_types.h"
 #include "src/trace_processor/containers/null_term_string_view.h"
 
@@ -79,8 +79,8 @@
 
 // Parses a string containing a type from SQL and converts it to a Type enum
 // value.
-// Returns base::nullopt if |type| did not correspond to any of the enum values.
-base::Optional<Type> ParseType(base::StringView type);
+// Returns std::nullopt if |type| did not correspond to any of the enum values.
+std::optional<Type> ParseType(base::StringView type);
 
 // Converts an argument type to a string for printing (e.g. in error messages
 // etc).
diff --git a/src/trace_processor/util/sql_argument_unittest.cc b/src/trace_processor/util/sql_argument_unittest.cc
index f75ceea..9f15859 100644
--- a/src/trace_processor/util/sql_argument_unittest.cc
+++ b/src/trace_processor/util/sql_argument_unittest.cc
@@ -49,7 +49,7 @@
 TEST(SqlArgumentTest, ParseType) {
   ASSERT_EQ(ParseType("PROTO"), Type::kProto);
   ASSERT_EQ(ParseType("BOOL"), Type::kBool);
-  ASSERT_EQ(ParseType("UNKNOWN"), base::nullopt);
+  ASSERT_EQ(ParseType("UNKNOWN"), std::nullopt);
   ASSERT_EQ(ParseType("UINT"), Type::kUint);
 }
 
diff --git a/src/traceconv/pprof_builder.cc b/src/traceconv/pprof_builder.cc
index 07aa6f9..1ca8654 100644
--- a/src/traceconv/pprof_builder.cc
+++ b/src/traceconv/pprof_builder.cc
@@ -159,10 +159,10 @@
   return ret;
 }
 
-base::Optional<int64_t> GetStatsEntry(
+std::optional<int64_t> GetStatsEntry(
     trace_processor::TraceProcessor* tp,
     const std::string& name,
-    base::Optional<uint64_t> idx = base::nullopt) {
+    std::optional<uint64_t> idx = std::nullopt) {
   std::string query = "select value from stats where name == '" + name + "'";
   if (idx.has_value())
     query += " and idx == " + std::to_string(idx.value());
@@ -172,12 +172,12 @@
     if (!it.Status().ok()) {
       PERFETTO_DFATAL_OR_ELOG("Invalid iterator: %s",
                               it.Status().message().c_str());
-      return base::nullopt;
+      return std::nullopt;
     }
     // some stats are not present unless non-zero
-    return base::make_optional(0);
+    return std::make_optional(0);
   }
-  return base::make_optional(it.Get(0).AsLong());
+  return std::make_optional(it.Get(0).AsLong());
 }
 
 // Interns Locations, Lines, and Functions. Interning is done by the entity's
@@ -340,9 +340,9 @@
       int64_t mapping_id = c_it.Get(2).AsLong();
       auto func_sysname = c_it.Get(3).is_null() ? "" : c_it.Get(3).AsString();
       auto func_name = c_it.Get(4).is_null() ? "" : c_it.Get(4).AsString();
-      base::Optional<int64_t> symbol_set_id =
-          c_it.Get(5).is_null() ? base::nullopt
-                                : base::make_optional(c_it.Get(5).AsLong());
+      std::optional<int64_t> symbol_set_id =
+          c_it.Get(5).is_null() ? std::nullopt
+                                : std::make_optional(c_it.Get(5).AsLong());
 
       Location loc(mapping_id, /*single_function_id=*/-1, {});
 
@@ -661,8 +661,8 @@
 
 static bool VerifyPIDStats(trace_processor::TraceProcessor* tp, uint64_t pid) {
   bool success = true;
-  base::Optional<int64_t> stat =
-      GetStatsEntry(tp, "heapprofd_buffer_corrupted", base::make_optional(pid));
+  std::optional<int64_t> stat =
+      GetStatsEntry(tp, "heapprofd_buffer_corrupted", std::make_optional(pid));
   if (!stat.has_value()) {
     PERFETTO_DFATAL_OR_ELOG("Failed to get heapprofd_buffer_corrupted stat");
   } else if (stat.value() > 0) {
@@ -673,8 +673,7 @@
                   " CLIENT MEMORY CORRUPTION.",
                   pid);
   }
-  stat =
-      GetStatsEntry(tp, "heapprofd_buffer_overran", base::make_optional(pid));
+  stat = GetStatsEntry(tp, "heapprofd_buffer_overran", std::make_optional(pid));
   if (!stat.has_value()) {
     PERFETTO_DFATAL_OR_ELOG("Failed to get heapprofd_buffer_overran stat");
   } else if (stat.value() > 0) {
@@ -861,7 +860,7 @@
 }
 
 static void LogTracePerfEventIssues(trace_processor::TraceProcessor* tp) {
-  base::Optional<int64_t> stat = GetStatsEntry(tp, "perf_samples_skipped");
+  std::optional<int64_t> stat = GetStatsEntry(tp, "perf_samples_skipped");
   if (!stat.has_value()) {
     PERFETTO_DFATAL_OR_ELOG("Failed to look up perf_samples_skipped stat");
   } else if (stat.value() > 0) {
diff --git a/src/traceconv/trace_to_hprof.cc b/src/traceconv/trace_to_hprof.cc
index d60e193..5bf1b01 100644
--- a/src/traceconv/trace_to_hprof.cc
+++ b/src/traceconv/trace_to_hprof.cc
@@ -18,6 +18,7 @@
 
 #include <algorithm>
 #include <limits>
+#include <optional>
 #include <string>
 #include <unordered_map>
 #include <unordered_set>
@@ -25,7 +26,6 @@
 
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/endian.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "src/traceconv/utils.h"
 
@@ -141,7 +141,7 @@
 // until S.
 class RawClassData {
  public:
-  void AddClass(uint64_t id, base::Optional<uint64_t> superclass_id) {
+  void AddClass(uint64_t id, std::optional<uint64_t> superclass_id) {
     ids_.push_back(std::make_pair(id, superclass_id));
   }
 
@@ -162,7 +162,7 @@
 
  private:
   // Pair contains class ID and super class ID.
-  std::vector<std::pair<uint64_t, base::Optional<uint64_t>>> ids_;
+  std::vector<std::pair<uint64_t, std::optional<uint64_t>>> ids_;
   // Class id of the template
   std::vector<uint64_t> template_ids_;
 };
@@ -217,10 +217,10 @@
       uint64_t name_id = IngestString(dname);
 
       auto raw_super_id = it.Get(2);
-      base::Optional<uint64_t> maybe_super_id =
+      std::optional<uint64_t> maybe_super_id =
           raw_super_id.is_null()
-              ? base::nullopt
-              : base::Optional<uint64_t>(
+              ? std::nullopt
+              : std::optional<uint64_t>(
                     static_cast<uint64_t>(raw_super_id.AsLong()));
 
       std::string location(it.Get(3).AsString());
diff --git a/src/traceconv/utils.cc b/src/traceconv/utils.cc
index d223d8d..1789bc4 100644
--- a/src/traceconv/utils.cc
+++ b/src/traceconv/utils.cc
@@ -20,13 +20,13 @@
 
 #include <cinttypes>
 #include <memory>
+#include <optional>
 #include <ostream>
 #include <set>
 #include <utility>
 
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/file_utils.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/string_splitter.h"
 #include "perfetto/protozero/scattered_heap_buffer.h"
diff --git a/src/traceconv/utils.h b/src/traceconv/utils.h
index ce9f8ce..a261774 100644
--- a/src/traceconv/utils.h
+++ b/src/traceconv/utils.h
@@ -22,10 +22,10 @@
 #include <functional>
 #include <iostream>
 #include <memory>
+#include <optional>
 #include <vector>
 
 #include "perfetto/base/build_config.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/paged_memory.h"
 #include "src/profiling/deobfuscator.h"
 
diff --git a/src/traced/probes/android_game_intervention_list/android_game_intervention_list_data_source.cc b/src/traced/probes/android_game_intervention_list/android_game_intervention_list_data_source.cc
index 527195e..b3aa454 100644
--- a/src/traced/probes/android_game_intervention_list/android_game_intervention_list_data_source.cc
+++ b/src/traced/probes/android_game_intervention_list/android_game_intervention_list_data_source.cc
@@ -17,9 +17,9 @@
 #include "src/traced/probes/android_game_intervention_list/android_game_intervention_list_data_source.h"
 
 #include <stddef.h>
+#include <optional>
 
 #include "perfetto/base/logging.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/string_splitter.h"
 #include "perfetto/ext/base/string_utils.h"
@@ -140,9 +140,9 @@
         break;
       }
       case 1: {
-        base::Optional<uint64_t> uid =
+        std::optional<uint64_t> uid =
             base::CStringToUInt64(string_splitter.cur_token());
-        if (uid == base::nullopt) {
+        if (uid == std::nullopt) {
           PERFETTO_DLOG("Failed to parse game_mode_intervention.list uid.");
           return false;
         }
@@ -150,9 +150,9 @@
         break;
       }
       case 2: {
-        base::Optional<uint32_t> cur_mode =
+        std::optional<uint32_t> cur_mode =
             base::CStringToUInt32(string_splitter.cur_token());
-        if (cur_mode == base::nullopt) {
+        if (cur_mode == std::nullopt) {
           PERFETTO_DLOG(
               "Failed to parse game_mode_intervention.list cur_mode.");
           return false;
@@ -163,9 +163,9 @@
       case 3:
       case 5:
       case 7: {
-        base::Optional<uint32_t> game_mode =
+        std::optional<uint32_t> game_mode =
             base::CStringToUInt32(string_splitter.cur_token());
-        if (game_mode == base::nullopt) {
+        if (game_mode == std::nullopt) {
           PERFETTO_DLOG(
               "Failed to parse game_mode_intervention.list game_mode.");
           return false;
@@ -186,9 +186,9 @@
           char* key = value_splitter.cur_token();
           if (strcmp(key, "angle") == 0) {
             value_splitter.Next();
-            base::Optional<uint32_t> use_angle =
+            std::optional<uint32_t> use_angle =
                 base::CStringToUInt32(value_splitter.cur_token());
-            if (use_angle == base::nullopt) {
+            if (use_angle == std::nullopt) {
               PERFETTO_DLOG(
                   "Failed to parse game_mode_intervention.list use_angle.");
               return false;
@@ -196,9 +196,9 @@
             game_mode_info->set_use_angle(use_angle.value());
           } else if (strcmp(key, "scaling") == 0) {
             value_splitter.Next();
-            base::Optional<double> resolution_downscale =
+            std::optional<double> resolution_downscale =
                 base::CStringToDouble(value_splitter.cur_token());
-            if (resolution_downscale == base::nullopt) {
+            if (resolution_downscale == std::nullopt) {
               PERFETTO_DLOG(
                   "Failed to parse game_mode_intervention.list "
                   "resolution_downscale.");
@@ -208,9 +208,9 @@
                 static_cast<float>(resolution_downscale.value()));
           } else if (strcmp(key, "fps") == 0) {
             value_splitter.Next();
-            base::Optional<double> fps =
+            std::optional<double> fps =
                 base::CStringToDouble(value_splitter.cur_token());
-            if (fps == base::nullopt) {
+            if (fps == std::nullopt) {
               PERFETTO_DLOG("Failed to parse game_mode_intervention.list fps.");
               return false;
             }
diff --git a/src/traced/probes/android_log/android_log_data_source.cc b/src/traced/probes/android_log/android_log_data_source.cc
index 2d4d264..f53cda5 100644
--- a/src/traced/probes/android_log/android_log_data_source.cc
+++ b/src/traced/probes/android_log/android_log_data_source.cc
@@ -15,12 +15,12 @@
  */
 
 #include "src/traced/probes/android_log/android_log_data_source.h"
+#include <optional>
 
 #include "perfetto/base/logging.h"
 #include "perfetto/base/task_runner.h"
 #include "perfetto/base/time.h"
 #include "perfetto/ext/base/file_utils.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/string_splitter.h"
 #include "perfetto/ext/base/string_view.h"
diff --git a/src/traced/probes/android_system_property/android_system_property_data_source.cc b/src/traced/probes/android_system_property/android_system_property_data_source.cc
index b9ba2b8..2cf351e 100644
--- a/src/traced/probes/android_system_property/android_system_property_data_source.cc
+++ b/src/traced/probes/android_system_property/android_system_property_data_source.cc
@@ -15,11 +15,11 @@
  */
 
 #include "src/traced/probes/android_system_property/android_system_property_data_source.h"
+#include <optional>
 
 #include "perfetto/base/task_runner.h"
 #include "perfetto/base/time.h"
 #include "perfetto/ext/base/android_utils.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/tracing/core/data_source_config.h"
 
@@ -100,7 +100,7 @@
   packet->set_timestamp(static_cast<uint64_t>(base::GetBootTimeNs().count()));
   auto* properties = packet->set_android_system_property();
   for (const auto& name : property_names_) {
-    const base::Optional<std::string> value = ReadProperty(name);
+    const std::optional<std::string> value = ReadProperty(name);
     if (value) {
       auto* property = properties->add_values();
       property->set_name(name);
@@ -118,20 +118,20 @@
 }
 
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-const base::Optional<std::string> AndroidSystemPropertyDataSource::ReadProperty(
+const std::optional<std::string> AndroidSystemPropertyDataSource::ReadProperty(
     const std::string& name) {
   std::string value = base::GetAndroidProp(name.c_str());
   if (value.empty()) {
     PERFETTO_LOG("Unable to read %s", name.c_str());
-    return base::nullopt;
+    return std::nullopt;
   }
-  return base::make_optional(value);
+  return std::make_optional(value);
 }
 #else
-const base::Optional<std::string> AndroidSystemPropertyDataSource::ReadProperty(
+const std::optional<std::string> AndroidSystemPropertyDataSource::ReadProperty(
     const std::string&) {
   PERFETTO_ELOG("Android System Properties only supported on Android.");
-  return base::nullopt;
+  return std::nullopt;
 }
 #endif
 
diff --git a/src/traced/probes/android_system_property/android_system_property_data_source.h b/src/traced/probes/android_system_property/android_system_property_data_source.h
index ec7db0e..04dd3e5 100644
--- a/src/traced/probes/android_system_property/android_system_property_data_source.h
+++ b/src/traced/probes/android_system_property/android_system_property_data_source.h
@@ -18,9 +18,9 @@
 #define SRC_TRACED_PROBES_ANDROID_SYSTEM_PROPERTY_ANDROID_SYSTEM_PROPERTY_DATA_SOURCE_H_
 
 #include <memory>
+#include <optional>
 #include <vector>
 
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/weak_ptr.h"
 #include "perfetto/ext/tracing/core/trace_writer.h"
 #include "src/traced/probes/probes_data_source.h"
@@ -45,7 +45,7 @@
   void Flush(FlushRequestID, std::function<void()> callback) override;
 
   // Virtual for testing.
-  virtual const base::Optional<std::string> ReadProperty(
+  virtual const std::optional<std::string> ReadProperty(
       const std::string& name);
 
  private:
diff --git a/src/traced/probes/android_system_property/android_system_property_data_source_unittest.cc b/src/traced/probes/android_system_property/android_system_property_data_source_unittest.cc
index 89d89e6..0014e49 100644
--- a/src/traced/probes/android_system_property/android_system_property_data_source_unittest.cc
+++ b/src/traced/probes/android_system_property/android_system_property_data_source_unittest.cc
@@ -45,7 +45,7 @@
                                         std::move(writer)) {}
 
   MOCK_METHOD1(ReadProperty,
-               const base::Optional<std::string>(const std::string&));
+               const std::optional<std::string>(const std::string&));
 };
 
 class AndroidSystemPropertyDataSourceTest : public ::testing::Test {
@@ -79,9 +79,9 @@
   auto data_source = CreateAndroidSystemPropertyDataSource(BuildConfig(
       {"debug.tracing.screen_state", "debug.tracing.screen_brightness"}));
   EXPECT_CALL(*data_source, ReadProperty("debug.tracing.screen_state"))
-      .WillOnce(Return(base::make_optional("2")));
+      .WillOnce(Return(std::make_optional("2")));
   EXPECT_CALL(*data_source, ReadProperty("debug.tracing.screen_brightness"))
-      .WillOnce(Return(base::make_optional("0.123456")));
+      .WillOnce(Return(std::make_optional("0.123456")));
   data_source->Start();
 
   protos::gen::TracePacket packet = writer_raw_->GetOnlyTracePacket();
@@ -112,9 +112,9 @@
   auto data_source = CreateAndroidSystemPropertyDataSource(BuildConfig(
       {"debug.tracing.screen_state", "debug.tracing.screen_brightness"}));
   EXPECT_CALL(*data_source, ReadProperty("debug.tracing.screen_state"))
-      .WillOnce(Return(base::nullopt));
+      .WillOnce(Return(std::nullopt));
   EXPECT_CALL(*data_source, ReadProperty("debug.tracing.screen_brightness"))
-      .WillOnce(Return(base::nullopt));
+      .WillOnce(Return(std::nullopt));
   data_source->Start();
 
   protos::gen::TracePacket packet = writer_raw_->GetOnlyTracePacket();
diff --git a/src/traced/probes/ftrace/atrace_wrapper.cc b/src/traced/probes/ftrace/atrace_wrapper.cc
index 632c131..7254325 100644
--- a/src/traced/probes/ftrace/atrace_wrapper.cc
+++ b/src/traced/probes/ftrace/atrace_wrapper.cc
@@ -24,12 +24,12 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <unistd.h>
+#include <optional>
 
 #include "perfetto/base/build_config.h"
 #include "perfetto/base/logging.h"
 #include "perfetto/base/time.h"
 #include "perfetto/ext/base/android_utils.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/pipe.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/utils.h"
@@ -39,7 +39,7 @@
 namespace {
 
 RunAtraceFunction g_run_atrace_for_testing = nullptr;
-base::Optional<bool> g_is_old_atrace_for_testing{};
+std::optional<bool> g_is_old_atrace_for_testing{};
 
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
 // Args should include "atrace" for argv[0].
diff --git a/src/traced/probes/ftrace/compact_sched.cc b/src/traced/probes/ftrace/compact_sched.cc
index cd2d117..16cb848 100644
--- a/src/traced/probes/ftrace/compact_sched.cc
+++ b/src/traced/probes/ftrace/compact_sched.cc
@@ -17,8 +17,8 @@
 #include "src/traced/probes/ftrace/compact_sched.h"
 
 #include <stdint.h>
+#include <optional>
 
-#include "perfetto/ext/base/optional.h"
 #include "protos/perfetto/config/ftrace/ftrace_config.gen.h"
 #include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
 #include "protos/perfetto/trace/ftrace/sched.pbzero.h"
@@ -32,7 +32,7 @@
 // Pre-parse the format of sched_switch, checking if our simplifying
 // assumptions about possible widths/signedness hold, and record the subset
 // of the format that will be used during parsing.
-base::Optional<CompactSchedSwitchFormat> ValidateSchedSwitchFormat(
+std::optional<CompactSchedSwitchFormat> ValidateSchedSwitchFormat(
     const Event& event) {
   using protos::pbzero::SchedSwitchFtraceEvent;
 
@@ -85,15 +85,15 @@
 
   if (!prev_state_valid || !next_pid_valid || !next_prio_valid ||
       !next_comm_valid) {
-    return base::nullopt;
+    return std::nullopt;
   }
-  return base::make_optional(switch_format);
+  return std::make_optional(switch_format);
 }
 
 // Pre-parse the format of sched_waking, checking if our simplifying
 // assumptions about possible widths/signedness hold, and record the subset
 // of the format that will be used during parsing.
-base::Optional<CompactSchedWakingFormat> ValidateSchedWakingFormat(
+std::optional<CompactSchedWakingFormat> ValidateSchedWakingFormat(
     const Event& event) {
   using protos::pbzero::SchedWakingFtraceEvent;
 
@@ -143,9 +143,9 @@
   }
 
   if (!pid_valid || !target_cpu_valid || !prio_valid || !comm_valid) {
-    return base::nullopt;
+    return std::nullopt;
   }
-  return base::make_optional(waking_format);
+  return std::make_optional(waking_format);
 }
 
 }  // namespace
@@ -157,8 +157,8 @@
     const std::vector<Event>& events) {
   using protos::pbzero::FtraceEvent;
 
-  base::Optional<CompactSchedSwitchFormat> switch_format;
-  base::Optional<CompactSchedWakingFormat> waking_format;
+  std::optional<CompactSchedSwitchFormat> switch_format;
+  std::optional<CompactSchedWakingFormat> waking_format;
   for (const Event& event : events) {
     if (event.proto_field_id == FtraceEvent::kSchedSwitchFieldNumber) {
       switch_format = ValidateSchedSwitchFormat(event);
diff --git a/src/traced/probes/ftrace/cpu_reader.cc b/src/traced/probes/ftrace/cpu_reader.cc
index b4c1758..c38627c 100644
--- a/src/traced/probes/ftrace/cpu_reader.cc
+++ b/src/traced/probes/ftrace/cpu_reader.cc
@@ -21,12 +21,12 @@
 #include <signal.h>
 
 #include <algorithm>
+#include <optional>
 #include <utility>
 
 #include "perfetto/base/build_config.h"
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/metatrace.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_splitter.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/utils.h"
@@ -267,7 +267,7 @@
       // prematurely.
       static constexpr size_t kRoughlyAPage = base::kPageSize - 512;
       const uint8_t* scratch_ptr = curr_page;
-      base::Optional<PageHeader> hdr =
+      std::optional<PageHeader> hdr =
           ParsePageHeader(&scratch_ptr, table_->page_header_size_len());
       PERFETTO_DCHECK(hdr && hdr->size > 0 && hdr->size <= base::kPageSize);
       if (!hdr.has_value()) {
@@ -426,7 +426,7 @@
     const uint8_t* curr_page = parsing_buf + (pages_parsed * base::kPageSize);
     const uint8_t* curr_page_end = curr_page + base::kPageSize;
     const uint8_t* parse_pos = curr_page;
-    base::Optional<PageHeader> page_header =
+    std::optional<PageHeader> page_header =
         ParsePageHeader(&parse_pos, table->page_header_size_len());
 
     if (!page_header.has_value() || page_header->size == 0 ||
@@ -477,7 +477,7 @@
 // flag (RB_MISSED_STORED).
 //
 // static
-base::Optional<CpuReader::PageHeader> CpuReader::ParsePageHeader(
+std::optional<CpuReader::PageHeader> CpuReader::ParsePageHeader(
     const uint8_t** ptr,
     uint16_t page_header_size_len) {
   // Mask for the data length portion of the |commit| field. Note that the
@@ -493,7 +493,7 @@
   PageHeader page_header;
   if (!CpuReader::ReadAndAdvance<uint64_t>(ptr, end_of_page,
                                            &page_header.timestamp))
-    return base::nullopt;
+    return std::nullopt;
 
   uint32_t size_and_flags;
 
@@ -501,7 +501,7 @@
   // number later.
   if (!CpuReader::ReadAndAdvance<uint32_t>(
           ptr, end_of_page, base::AssumeLittleEndian(&size_and_flags)))
-    return base::nullopt;
+    return std::nullopt;
 
   page_header.size = size_and_flags & kDataSizeMask;
   page_header.lost_events = bool(size_and_flags & kMissedEventsFlag);
@@ -513,7 +513,7 @@
   PERFETTO_DCHECK(page_header_size_len >= 4);
   *ptr += page_header_size_len - 4;
 
-  return base::make_optional(page_header);
+  return std::make_optional(page_header);
 }
 
 // A raw ftrace buffer page consists of a header followed by a sequence of
diff --git a/src/traced/probes/ftrace/cpu_reader.h b/src/traced/probes/ftrace/cpu_reader.h
index e0953d6..91e54ee 100644
--- a/src/traced/probes/ftrace/cpu_reader.h
+++ b/src/traced/probes/ftrace/cpu_reader.h
@@ -23,10 +23,10 @@
 #include <array>
 #include <atomic>
 #include <memory>
+#include <optional>
 #include <set>
 #include <thread>
 
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/paged_memory.h"
 #include "perfetto/ext/base/pipe.h"
 #include "perfetto/ext/base/scoped_file.h"
@@ -174,7 +174,7 @@
   }
 
   // Returns a parsed representation of the given raw ftrace page's header.
-  static base::Optional<CpuReader::PageHeader> ParsePageHeader(
+  static std::optional<CpuReader::PageHeader> ParsePageHeader(
       const uint8_t** ptr,
       uint16_t page_header_size_len);
 
diff --git a/src/traced/probes/ftrace/cpu_reader_benchmark.cc b/src/traced/probes/ftrace/cpu_reader_benchmark.cc
index a029bc7..8b487b6 100644
--- a/src/traced/probes/ftrace/cpu_reader_benchmark.cc
+++ b/src/traced/probes/ftrace/cpu_reader_benchmark.cc
@@ -823,7 +823,7 @@
 
 void DoParse(const ExamplePage& test_case,
              const std::vector<GroupAndName>& enabled_events,
-             base::Optional<FtraceConfig::PrintFilter> print_filter,
+             std::optional<FtraceConfig::PrintFilter> print_filter,
              benchmark::State& state) {
   ScatteredStreamWriterNullDelegate delegate(base::kPageSize);
   ScatteredStreamWriter stream(&delegate);
@@ -835,7 +835,7 @@
   FtraceDataSourceConfig ds_config{EventFilter{},
                                    EventFilter{},
                                    DisabledCompactSchedConfigForTesting(),
-                                   base::nullopt,
+                                   std::nullopt,
                                    {},
                                    {},
                                    false /*symbolize_ksyms*/,
@@ -858,7 +858,7 @@
     std::unique_ptr<CompactSchedBuffer> compact_buffer(
         new CompactSchedBuffer());
     const uint8_t* parse_pos = page.get();
-    base::Optional<CpuReader::PageHeader> page_header =
+    std::optional<CpuReader::PageHeader> page_header =
         CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
 
     if (!page_header.has_value())
@@ -874,12 +874,12 @@
 
 void BM_ParsePageFullOfSchedSwitch(benchmark::State& state) {
   DoParse(g_full_page_sched_switch, {GroupAndName("sched", "sched_switch")},
-          base::nullopt, state);
+          std::nullopt, state);
 }
 BENCHMARK(BM_ParsePageFullOfSchedSwitch);
 
 void BM_ParsePageFullOfPrint(benchmark::State& state) {
-  DoParse(g_full_page_print, {GroupAndName("ftrace", "print")}, base::nullopt,
+  DoParse(g_full_page_print, {GroupAndName("ftrace", "print")}, std::nullopt,
           state);
 }
 BENCHMARK(BM_ParsePageFullOfPrint);
@@ -898,7 +898,7 @@
 
 void BM_ParsePageFullOfAtracePrint(benchmark::State& state) {
   DoParse(g_full_page_atrace_print, {GroupAndName("ftrace", "print")},
-          base::nullopt, state);
+          std::nullopt, state);
 }
 BENCHMARK(BM_ParsePageFullOfAtracePrint);
 
diff --git a/src/traced/probes/ftrace/cpu_reader_fuzzer.cc b/src/traced/probes/ftrace/cpu_reader_fuzzer.cc
index 76908c9..f9dd522 100644
--- a/src/traced/probes/ftrace/cpu_reader_fuzzer.cc
+++ b/src/traced/probes/ftrace/cpu_reader_fuzzer.cc
@@ -57,7 +57,7 @@
   FtraceDataSourceConfig ds_config{EventFilter{},
                                    EventFilter{},
                                    DisabledCompactSchedConfigForTesting(),
-                                   base::nullopt,
+                                   std::nullopt,
                                    {},
                                    {},
                                    /*symbolize_ksyms=*/false,
diff --git a/src/traced/probes/ftrace/cpu_reader_unittest.cc b/src/traced/probes/ftrace/cpu_reader_unittest.cc
index 2466109..ba77921 100644
--- a/src/traced/probes/ftrace/cpu_reader_unittest.cc
+++ b/src/traced/probes/ftrace/cpu_reader_unittest.cc
@@ -68,7 +68,7 @@
   return FtraceDataSourceConfig{EventFilter{},
                                 EventFilter{},
                                 DisabledCompactSchedConfigForTesting(),
-                                base::nullopt,
+                                std::nullopt,
                                 {},
                                 {},
                                 false /*symbolize_ksyms*/,
@@ -393,7 +393,7 @@
   FtraceMetadata metadata{};
   std::unique_ptr<CompactSchedBuffer> compact_buffer(new CompactSchedBuffer());
   const uint8_t* parse_pos = page.get();
-  base::Optional<CpuReader::PageHeader> page_header =
+  std::optional<CpuReader::PageHeader> page_header =
       CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
 
   const uint8_t* page_end = page.get() + base::kPageSize;
@@ -521,7 +521,7 @@
   std::unique_ptr<CompactSchedBuffer> compact_buffer(new CompactSchedBuffer());
 
   const uint8_t* parse_pos = page.get();
-  base::Optional<CpuReader::PageHeader> page_header =
+  std::optional<CpuReader::PageHeader> page_header =
       CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
 
   const uint8_t* page_end = page.get() + base::kPageSize;
@@ -569,7 +569,7 @@
   FtraceMetadata metadata{};
   std::unique_ptr<CompactSchedBuffer> compact_buffer(new CompactSchedBuffer());
   const uint8_t* parse_pos = page.get();
-  base::Optional<CpuReader::PageHeader> page_header =
+  std::optional<CpuReader::PageHeader> page_header =
       CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
 
   const uint8_t* page_end = page.get() + base::kPageSize;
@@ -617,7 +617,7 @@
   FtraceMetadata metadata{};
   std::unique_ptr<CompactSchedBuffer> compact_buffer(new CompactSchedBuffer());
   const uint8_t* parse_pos = page.get();
-  base::Optional<CpuReader::PageHeader> page_header =
+  std::optional<CpuReader::PageHeader> page_header =
       CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
 
   const uint8_t* page_end = page.get() + base::kPageSize;
@@ -653,7 +653,7 @@
   FtraceMetadata metadata{};
   std::unique_ptr<CompactSchedBuffer> compact_buffer(new CompactSchedBuffer());
   const uint8_t* parse_pos = page.get();
-  base::Optional<CpuReader::PageHeader> page_header =
+  std::optional<CpuReader::PageHeader> page_header =
       CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
 
   ASSERT_TRUE(page_header.has_value());
@@ -718,7 +718,7 @@
   FtraceMetadata metadata{};
   std::unique_ptr<CompactSchedBuffer> compact_buffer(new CompactSchedBuffer());
   const uint8_t* parse_pos = page.get();
-  base::Optional<CpuReader::PageHeader> page_header =
+  std::optional<CpuReader::PageHeader> page_header =
       CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
 
   const uint8_t* page_end = page.get() + base::kPageSize;
@@ -767,7 +767,7 @@
   FtraceMetadata metadata{};
   std::unique_ptr<CompactSchedBuffer> compact_buffer(new CompactSchedBuffer());
   const uint8_t* parse_pos = page.get();
-  base::Optional<CpuReader::PageHeader> page_header =
+  std::optional<CpuReader::PageHeader> page_header =
       CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
 
   const uint8_t* page_end = page.get() + base::kPageSize;
@@ -907,7 +907,7 @@
   FtraceMetadata metadata{};
   std::unique_ptr<CompactSchedBuffer> compact_buffer(new CompactSchedBuffer());
   const uint8_t* parse_pos = page.get();
-  base::Optional<CpuReader::PageHeader> page_header =
+  std::optional<CpuReader::PageHeader> page_header =
       CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
 
   const uint8_t* page_end = page.get() + base::kPageSize;
@@ -948,7 +948,7 @@
   FtraceDataSourceConfig ds_config{EventFilter{},
                                    EventFilter{},
                                    EnabledCompactSchedConfigForTesting(),
-                                   base::nullopt,
+                                   std::nullopt,
                                    {},
                                    {},
                                    false /* symbolize_ksyms*/,
@@ -960,7 +960,7 @@
   FtraceMetadata metadata{};
   std::unique_ptr<CompactSchedBuffer> compact_buffer(new CompactSchedBuffer());
   const uint8_t* parse_pos = page.get();
-  base::Optional<CpuReader::PageHeader> page_header =
+  std::optional<CpuReader::PageHeader> page_header =
       CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
 
   const uint8_t* page_end = page.get() + base::kPageSize;
@@ -1705,7 +1705,7 @@
   FtraceMetadata metadata{};
   std::unique_ptr<CompactSchedBuffer> compact_buffer(new CompactSchedBuffer());
   const uint8_t* parse_pos = page.get();
-  base::Optional<CpuReader::PageHeader> page_header =
+  std::optional<CpuReader::PageHeader> page_header =
       CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
 
   const uint8_t* page_end = page.get() + base::kPageSize;
@@ -2193,7 +2193,7 @@
   FtraceMetadata metadata{};
   std::unique_ptr<CompactSchedBuffer> compact_buffer(new CompactSchedBuffer());
   const uint8_t* parse_pos = page.get();
-  base::Optional<CpuReader::PageHeader> page_header =
+  std::optional<CpuReader::PageHeader> page_header =
       CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
 
   const uint8_t* page_end = page.get() + base::kPageSize;
@@ -2281,7 +2281,7 @@
   FtraceMetadata metadata{};
   std::unique_ptr<CompactSchedBuffer> compact_buffer(new CompactSchedBuffer());
   const uint8_t* parse_pos = page.get();
-  base::Optional<CpuReader::PageHeader> page_header =
+  std::optional<CpuReader::PageHeader> page_header =
       CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
   ASSERT_TRUE(page_header.has_value());
 
@@ -2725,7 +2725,7 @@
   FtraceMetadata metadata{};
   std::unique_ptr<CompactSchedBuffer> compact_buffer(new CompactSchedBuffer());
   const uint8_t* parse_pos = page.get();
-  base::Optional<CpuReader::PageHeader> page_header =
+  std::optional<CpuReader::PageHeader> page_header =
       CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
 
   const uint8_t* page_end = page.get() + base::kPageSize;
@@ -2834,7 +2834,7 @@
   FtraceMetadata metadata{};
   std::unique_ptr<CompactSchedBuffer> compact_buffer(new CompactSchedBuffer());
   const uint8_t* parse_pos = page.get();
-  base::Optional<CpuReader::PageHeader> page_header =
+  std::optional<CpuReader::PageHeader> page_header =
       CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
 
   const uint8_t* page_end = page.get() + base::kPageSize;
@@ -3140,7 +3140,7 @@
   FtraceMetadata metadata{};
   std::unique_ptr<CompactSchedBuffer> compact_buffer(new CompactSchedBuffer());
   const uint8_t* parse_pos = page.get();
-  base::Optional<CpuReader::PageHeader> page_header =
+  std::optional<CpuReader::PageHeader> page_header =
       CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
 
   const uint8_t* page_end = page.get() + base::kPageSize;
diff --git a/src/traced/probes/ftrace/ftrace_config_muxer.cc b/src/traced/probes/ftrace/ftrace_config_muxer.cc
index 61a26dd..0be1842 100644
--- a/src/traced/probes/ftrace/ftrace_config_muxer.cc
+++ b/src/traced/probes/ftrace/ftrace_config_muxer.cc
@@ -523,7 +523,7 @@
   }
 
   for (const std::string& syscall : request.syscall_events()) {
-    base::Optional<size_t> id = syscalls_.GetByName(syscall);
+    std::optional<size_t> id = syscalls_.GetByName(syscall);
     if (!id.has_value()) {
       PERFETTO_ELOG("Can't enable %s, syscall not known", syscall.c_str());
       continue;
@@ -744,7 +744,7 @@
   auto compact_sched =
       CreateCompactSchedConfig(request, table_->compact_sched_format());
 
-  base::Optional<FtracePrintFilterConfig> ftrace_print_filter;
+  std::optional<FtracePrintFilterConfig> ftrace_print_filter;
   if (request.has_print_filter()) {
     ftrace_print_filter =
         FtracePrintFilterConfig::Create(request.print_filter(), table_);
diff --git a/src/traced/probes/ftrace/ftrace_config_muxer.h b/src/traced/probes/ftrace/ftrace_config_muxer.h
index 49a5989..67176b2 100644
--- a/src/traced/probes/ftrace/ftrace_config_muxer.h
+++ b/src/traced/probes/ftrace/ftrace_config_muxer.h
@@ -18,9 +18,9 @@
 #define SRC_TRACED_PROBES_FTRACE_FTRACE_CONFIG_MUXER_H_
 
 #include <map>
+#include <optional>
 #include <set>
 
-#include "perfetto/ext/base/optional.h"
 #include "src/kernel_utils/syscall_table.h"
 #include "src/traced/probes/ftrace/compact_sched.h"
 #include "src/traced/probes/ftrace/ftrace_config_utils.h"
@@ -44,7 +44,7 @@
   FtraceDataSourceConfig(EventFilter _event_filter,
                          EventFilter _syscall_filter,
                          CompactSchedConfig _compact_sched,
-                         base::Optional<FtracePrintFilterConfig> _print_filter,
+                         std::optional<FtracePrintFilterConfig> _print_filter,
                          std::vector<std::string> _atrace_apps,
                          std::vector<std::string> _atrace_categories,
                          bool _symbolize_ksyms,
@@ -72,7 +72,7 @@
 
   // Optional configuration that's used to filter "ftrace/print" events based on
   // the content of their "buf" field.
-  base::Optional<FtracePrintFilterConfig> print_filter;
+  std::optional<FtracePrintFilterConfig> print_filter;
 
   // Used only in Android for ATRACE_EVENT/os.Trace() userspace annotations.
   std::vector<std::string> atrace_apps;
diff --git a/src/traced/probes/ftrace/ftrace_controller.cc b/src/traced/probes/ftrace/ftrace_controller.cc
index 49d4080..907e772 100644
--- a/src/traced/probes/ftrace/ftrace_controller.cc
+++ b/src/traced/probes/ftrace/ftrace_controller.cc
@@ -96,13 +96,13 @@
   return !!fd;
 }
 
-base::Optional<int64_t> ReadFtraceNowTs(const base::ScopedFile& cpu_stats_fd) {
+std::optional<int64_t> ReadFtraceNowTs(const base::ScopedFile& cpu_stats_fd) {
   PERFETTO_CHECK(cpu_stats_fd);
 
   char buf[512];
   ssize_t res = PERFETTO_EINTR(pread(*cpu_stats_fd, buf, sizeof(buf) - 1, 0));
   if (res <= 0)
-    return base::nullopt;
+    return std::nullopt;
   buf[res] = '\0';
 
   FtraceCpuStats stats{};
@@ -635,7 +635,7 @@
 // TODO(rsavitski): dedupe with FtraceController::Create.
 std::unique_ptr<FtraceController::FtraceInstanceState>
 FtraceController::CreateSecondaryInstance(const std::string& instance_name) {
-  base::Optional<std::string> instance_path = AbsolutePathForInstance(
+  std::optional<std::string> instance_path = AbsolutePathForInstance(
       primary_.ftrace_procfs->GetRootPath(), instance_name);
   if (!instance_path.has_value()) {
     PERFETTO_ELOG("Invalid ftrace instance name: \"%s\"",
@@ -675,12 +675,12 @@
 // to be careful to distinguish the tracefs mount point from the default
 // instance path.
 // static
-base::Optional<std::string> FtraceController::AbsolutePathForInstance(
+std::optional<std::string> FtraceController::AbsolutePathForInstance(
     const std::string& tracefs_root,
     const std::string& raw_cfg_name) {
   if (base::Contains(raw_cfg_name, '/') ||
       base::StartsWith(raw_cfg_name, "..")) {
-    return base::nullopt;
+    return std::nullopt;
   }
 
   // ARM64 pKVM hypervisor tracing emulates an instance, but is not under
@@ -690,7 +690,7 @@
     PERFETTO_LOG(
         "Config specified reserved \"hyp\" instance name, using %s for events.",
         hyp_path.c_str());
-    return base::make_optional(hyp_path);
+    return std::make_optional(hyp_path);
   }
 
   return tracefs_root + "instances/" + raw_cfg_name + "/";
diff --git a/src/traced/probes/ftrace/ftrace_controller.h b/src/traced/probes/ftrace/ftrace_controller.h
index 9318f2a..e4f3848 100644
--- a/src/traced/probes/ftrace/ftrace_controller.h
+++ b/src/traced/probes/ftrace/ftrace_controller.h
@@ -88,7 +88,7 @@
   }
 
   // public for testing
-  static base::Optional<std::string> AbsolutePathForInstance(
+  static std::optional<std::string> AbsolutePathForInstance(
       const std::string& tracefs_root,
       const std::string& raw_cfg_name);
 
diff --git a/src/traced/probes/ftrace/ftrace_controller_unittest.cc b/src/traced/probes/ftrace/ftrace_controller_unittest.cc
index 0d41004..02bb3cf 100644
--- a/src/traced/probes/ftrace/ftrace_controller_unittest.cc
+++ b/src/traced/probes/ftrace/ftrace_controller_unittest.cc
@@ -756,7 +756,7 @@
 }
 
 TEST(FtraceControllerTest, TracefsInstanceFilepaths) {
-  base::Optional<std::string> path;
+  std::optional<std::string> path;
   path = FtraceController::AbsolutePathForInstance("/root/", "test");
   EXPECT_EQ(*path, "/root/instances/test/");
 
diff --git a/src/traced/probes/ftrace/ftrace_print_filter.cc b/src/traced/probes/ftrace/ftrace_print_filter.cc
index 5080fca..316a017 100644
--- a/src/traced/probes/ftrace/ftrace_print_filter.cc
+++ b/src/traced/probes/ftrace/ftrace_print_filter.cc
@@ -108,12 +108,12 @@
 }
 
 // static
-base::Optional<FtracePrintFilterConfig> FtracePrintFilterConfig::Create(
+std::optional<FtracePrintFilterConfig> FtracePrintFilterConfig::Create(
     const protos::gen::FtraceConfig::PrintFilter& config,
     ProtoTranslationTable* table) {
   const Event* print_event = table->GetEvent(GroupAndName("ftrace", "print"));
   if (!print_event) {
-    return base::nullopt;
+    return std::nullopt;
   }
   const Field* buf_field = nullptr;
   for (const Field& field : print_event->fields) {
@@ -123,11 +123,11 @@
     }
   }
   if (!buf_field) {
-    return base::nullopt;
+    return std::nullopt;
   }
 
   if (buf_field->strategy != kCStringToString) {
-    return base::nullopt;
+    return std::nullopt;
   }
   FtracePrintFilterConfig ret{FtracePrintFilter{config}};
   ret.event_id_ = print_event->ftrace_event_id;
diff --git a/src/traced/probes/ftrace/ftrace_print_filter.h b/src/traced/probes/ftrace/ftrace_print_filter.h
index 707bb3e..419bcbf 100644
--- a/src/traced/probes/ftrace/ftrace_print_filter.h
+++ b/src/traced/probes/ftrace/ftrace_print_filter.h
@@ -17,10 +17,10 @@
 #ifndef SRC_TRACED_PROBES_FTRACE_FTRACE_PRINT_FILTER_H_
 #define SRC_TRACED_PROBES_FTRACE_FTRACE_PRINT_FILTER_H_
 
+#include <optional>
 #include <string>
 #include <vector>
 
-#include "perfetto/ext/base/optional.h"
 #include "src/traced/probes/ftrace/proto_translation_table.h"
 
 namespace perfetto {
@@ -62,7 +62,7 @@
 
 class FtracePrintFilterConfig {
  public:
-  static base::Optional<FtracePrintFilterConfig> Create(
+  static std::optional<FtracePrintFilterConfig> Create(
       const protos::gen::FtraceConfig_PrintFilter&,
       ProtoTranslationTable* table);
 
diff --git a/src/traced/probes/ftrace/ftrace_procfs.cc b/src/traced/probes/ftrace/ftrace_procfs.cc
index 7cf3b07..6112487 100644
--- a/src/traced/probes/ftrace/ftrace_procfs.cc
+++ b/src/traced/probes/ftrace/ftrace_procfs.cc
@@ -590,7 +590,7 @@
   if (str.size() && str[str.size() - 1] == '\n')
     str.resize(str.size() - 1);
 
-  base::Optional<uint32_t> id = base::StringToUInt32(str);
+  std::optional<uint32_t> id = base::StringToUInt32(str);
   if (!id)
     return 0;
   return *id;
diff --git a/src/traced/probes/ftrace/printk_formats_parser.cc b/src/traced/probes/ftrace/printk_formats_parser.cc
index 9a36dab..99dc856 100644
--- a/src/traced/probes/ftrace/printk_formats_parser.cc
+++ b/src/traced/probes/ftrace/printk_formats_parser.cc
@@ -19,10 +19,10 @@
 #include <stdio.h>
 
 #include <cinttypes>
+#include <optional>
 
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/file_utils.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_splitter.h"
 #include "perfetto/ext/base/string_utils.h"
 
@@ -52,7 +52,7 @@
     if (name.empty())
       continue;
 
-    base::Optional<uint64_t> address = base::StringToUInt64(raw_address, 16);
+    std::optional<uint64_t> address = base::StringToUInt64(raw_address, 16);
     if (address && address.value() != 0)
       mapping.insert(address.value(), name);
   }
diff --git a/src/traced/probes/initial_display_state/initial_display_state_data_source.cc b/src/traced/probes/initial_display_state/initial_display_state_data_source.cc
index d08cfd4..f7d3e58 100644
--- a/src/traced/probes/initial_display_state/initial_display_state_data_source.cc
+++ b/src/traced/probes/initial_display_state/initial_display_state_data_source.cc
@@ -15,11 +15,11 @@
  */
 
 #include "src/traced/probes/initial_display_state/initial_display_state_data_source.h"
+#include <optional>
 
 #include "perfetto/base/task_runner.h"
 #include "perfetto/base/time.h"
 #include "perfetto/ext/base/android_utils.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/tracing/core/data_source_config.h"
 
@@ -87,15 +87,15 @@
 void InitialDisplayStateDataSource::WriteState() {
   auto packet = writer_->NewTracePacket();
   packet->set_timestamp(static_cast<uint64_t>(base::GetBootTimeNs().count()));
-  const base::Optional<std::string> screen_state_str =
+  const std::optional<std::string> screen_state_str =
       ReadProperty("debug.tracing.screen_state");
-  const base::Optional<std::string> screen_brightness_str =
+  const std::optional<std::string> screen_brightness_str =
       ReadProperty("debug.tracing.screen_brightness");
-  const base::Optional<int> screen_state =
-      screen_state_str ? base::StringToInt32(*screen_state_str) : base::nullopt;
-  const base::Optional<double> screen_brightness =
+  const std::optional<int> screen_state =
+      screen_state_str ? base::StringToInt32(*screen_state_str) : std::nullopt;
+  const std::optional<double> screen_brightness =
       screen_brightness_str ? base::StringToDouble(*screen_brightness_str)
-                            : base::nullopt;
+                            : std::nullopt;
   if (screen_state || screen_brightness) {
     auto* state = packet->set_initial_display_state();
     if (screen_state) {
@@ -116,20 +116,20 @@
 }
 
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-const base::Optional<std::string> InitialDisplayStateDataSource::ReadProperty(
+const std::optional<std::string> InitialDisplayStateDataSource::ReadProperty(
     const std::string name) {
   std::string value = base::GetAndroidProp(name.c_str());
   if (value.empty()) {
     PERFETTO_ELOG("Unable to read %s", name.c_str());
-    return base::nullopt;
+    return std::nullopt;
   }
-  return base::make_optional(value);
+  return std::make_optional(value);
 }
 #else
-const base::Optional<std::string> InitialDisplayStateDataSource::ReadProperty(
+const std::optional<std::string> InitialDisplayStateDataSource::ReadProperty(
     const std::string name __attribute__((unused))) {
   PERFETTO_ELOG("Initial display state only supported on Android.");
-  return base::nullopt;
+  return std::nullopt;
 }
 #endif
 
diff --git a/src/traced/probes/initial_display_state/initial_display_state_data_source.h b/src/traced/probes/initial_display_state/initial_display_state_data_source.h
index b235c75..acb8789 100644
--- a/src/traced/probes/initial_display_state/initial_display_state_data_source.h
+++ b/src/traced/probes/initial_display_state/initial_display_state_data_source.h
@@ -18,8 +18,8 @@
 #define SRC_TRACED_PROBES_INITIAL_DISPLAY_STATE_INITIAL_DISPLAY_STATE_DATA_SOURCE_H_
 
 #include <memory>
+#include <optional>
 
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/weak_ptr.h"
 #include "perfetto/ext/tracing/core/trace_writer.h"
 #include "src/traced/probes/probes_data_source.h"
@@ -44,8 +44,7 @@
   void Flush(FlushRequestID, std::function<void()> callback) override;
 
   // Virtual for testing.
-  virtual const base::Optional<std::string> ReadProperty(
-      const std::string name);
+  virtual const std::optional<std::string> ReadProperty(const std::string name);
 
  private:
   void Tick();
diff --git a/src/traced/probes/initial_display_state/initial_display_state_data_source_unittest.cc b/src/traced/probes/initial_display_state/initial_display_state_data_source_unittest.cc
index f851ae6..6cb5f86 100644
--- a/src/traced/probes/initial_display_state/initial_display_state_data_source_unittest.cc
+++ b/src/traced/probes/initial_display_state/initial_display_state_data_source_unittest.cc
@@ -41,7 +41,7 @@
                                       std::move(writer)) {}
 
   MOCK_METHOD1(ReadProperty,
-               const base::Optional<std::string>(const std::string));
+               const std::optional<std::string>(const std::string));
 };
 
 class InitialDisplayStateDataSourceTest : public ::testing::Test {
@@ -65,9 +65,9 @@
   ASSERT_TRUE(true);
   auto data_source = GetInitialDisplayStateDataSource(DataSourceConfig());
   EXPECT_CALL(*data_source, ReadProperty("debug.tracing.screen_state"))
-      .WillOnce(Return(base::make_optional("2")));
+      .WillOnce(Return(std::make_optional("2")));
   EXPECT_CALL(*data_source, ReadProperty("debug.tracing.screen_brightness"))
-      .WillOnce(Return(base::make_optional("0.123456")));
+      .WillOnce(Return(std::make_optional("0.123456")));
   data_source->Start();
 
   protos::gen::TracePacket packet = writer_raw_->GetOnlyTracePacket();
@@ -81,9 +81,9 @@
   ASSERT_TRUE(true);
   auto data_source = GetInitialDisplayStateDataSource(DataSourceConfig());
   EXPECT_CALL(*data_source, ReadProperty("debug.tracing.screen_state"))
-      .WillOnce(Return(base::make_optional("2")));
+      .WillOnce(Return(std::make_optional("2")));
   EXPECT_CALL(*data_source, ReadProperty("debug.tracing.screen_brightness"))
-      .WillOnce(Return(base::make_optional("gotta wear shades")));
+      .WillOnce(Return(std::make_optional("gotta wear shades")));
   data_source->Start();
 
   protos::gen::TracePacket packet = writer_raw_->GetOnlyTracePacket();
@@ -97,9 +97,9 @@
   ASSERT_TRUE(true);
   auto data_source = GetInitialDisplayStateDataSource(DataSourceConfig());
   EXPECT_CALL(*data_source, ReadProperty("debug.tracing.screen_state"))
-      .WillOnce(Return(base::nullopt));
+      .WillOnce(Return(std::nullopt));
   EXPECT_CALL(*data_source, ReadProperty("debug.tracing.screen_brightness"))
-      .WillOnce(Return(base::nullopt));
+      .WillOnce(Return(std::nullopt));
   data_source->Start();
 
   protos::gen::TracePacket packet = writer_raw_->GetOnlyTracePacket();
diff --git a/src/traced/probes/power/android_power_data_source.cc b/src/traced/probes/power/android_power_data_source.cc
index 3f2866e..c67a8b2 100644
--- a/src/traced/probes/power/android_power_data_source.cc
+++ b/src/traced/probes/power/android_power_data_source.cc
@@ -16,12 +16,12 @@
 
 #include "src/traced/probes/power/android_power_data_source.h"
 
+#include <optional>
 #include <vector>
 
 #include "perfetto/base/logging.h"
 #include "perfetto/base/task_runner.h"
 #include "perfetto/base/time.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/tracing/core/trace_packet.h"
 #include "perfetto/ext/tracing/core/trace_writer.h"
@@ -70,13 +70,13 @@
   PERFETTO_LAZY_LOAD(android_internal::GetPowerEntityStateResidency,
                      get_power_entity_state_residency_);
 
-  base::Optional<int64_t> GetCounter(android_internal::BatteryCounter counter) {
+  std::optional<int64_t> GetCounter(android_internal::BatteryCounter counter) {
     if (!get_battery_counter_)
-      return base::nullopt;
+      return std::nullopt;
     int64_t value = 0;
     if (get_battery_counter_(counter, &value))
-      return base::make_optional(value);
-    return base::nullopt;
+      return std::make_optional(value);
+    return std::nullopt;
   }
 
   std::vector<android_internal::RailDescriptor> GetRailDescriptors() {
diff --git a/src/traced/probes/power/linux_power_sysfs_data_source.cc b/src/traced/probes/power/linux_power_sysfs_data_source.cc
index 9d4365b..ceeeec7 100644
--- a/src/traced/probes/power/linux_power_sysfs_data_source.cc
+++ b/src/traced/probes/power/linux_power_sysfs_data_source.cc
@@ -18,12 +18,12 @@
 
 #include <dirent.h>
 #include <sys/types.h>
+#include <optional>
 
 #include "perfetto/base/logging.h"
 #include "perfetto/base/task_runner.h"
 #include "perfetto/base/time.h"
 #include "perfetto/ext/base/file_utils.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/tracing/core/trace_packet.h"
@@ -38,10 +38,10 @@
 namespace {
 constexpr uint32_t kDefaultPollIntervalMs = 1000;
 
-base::Optional<int64_t> ReadFileAsInt64(std::string path) {
+std::optional<int64_t> ReadFileAsInt64(std::string path) {
   std::string buf;
   if (!base::ReadFile(path, &buf))
-    return base::nullopt;
+    return std::nullopt;
   return base::StringToInt64(base::StripSuffix(buf, "\n"));
 }
 }  // namespace
@@ -76,7 +76,7 @@
   return sysfs_battery_subdirs_.size();
 }
 
-base::Optional<int64_t>
+std::optional<int64_t>
 LinuxPowerSysfsDataSource::BatteryInfo::GetChargeCounterUah(
     size_t battery_idx) {
   PERFETTO_CHECK(battery_idx < sysfs_battery_subdirs_.size());
@@ -84,7 +84,7 @@
                          sysfs_battery_subdirs_[battery_idx] + "/charge_now");
 }
 
-base::Optional<int64_t>
+std::optional<int64_t>
 LinuxPowerSysfsDataSource::BatteryInfo::GetEnergyCounterUah(
     size_t battery_idx) {
   PERFETTO_CHECK(battery_idx < sysfs_battery_subdirs_.size());
@@ -92,28 +92,28 @@
                          sysfs_battery_subdirs_[battery_idx] + "/energy_now");
 }
 
-base::Optional<int64_t> LinuxPowerSysfsDataSource::BatteryInfo::GetVoltageUv(
+std::optional<int64_t> LinuxPowerSysfsDataSource::BatteryInfo::GetVoltageUv(
     size_t battery_idx) {
   PERFETTO_CHECK(battery_idx < sysfs_battery_subdirs_.size());
   return ReadFileAsInt64(power_supply_dir_path_ + "/" +
                          sysfs_battery_subdirs_[battery_idx] + "/voltage_now");
 }
 
-base::Optional<int64_t>
+std::optional<int64_t>
 LinuxPowerSysfsDataSource::BatteryInfo::GetCapacityPercent(size_t battery_idx) {
   PERFETTO_CHECK(battery_idx < sysfs_battery_subdirs_.size());
   return ReadFileAsInt64(power_supply_dir_path_ + "/" +
                          sysfs_battery_subdirs_[battery_idx] + "/capacity");
 }
 
-base::Optional<int64_t> LinuxPowerSysfsDataSource::BatteryInfo::GetCurrentNowUa(
+std::optional<int64_t> LinuxPowerSysfsDataSource::BatteryInfo::GetCurrentNowUa(
     size_t battery_idx) {
   PERFETTO_CHECK(battery_idx < sysfs_battery_subdirs_.size());
   return ReadFileAsInt64(power_supply_dir_path_ + "/" +
                          sysfs_battery_subdirs_[battery_idx] + "/current_now");
 }
 
-base::Optional<int64_t>
+std::optional<int64_t>
 LinuxPowerSysfsDataSource::BatteryInfo::GetAverageCurrentUa(
     size_t battery_idx) {
   PERFETTO_CHECK(battery_idx < sysfs_battery_subdirs_.size());
diff --git a/src/traced/probes/power/linux_power_sysfs_data_source.h b/src/traced/probes/power/linux_power_sysfs_data_source.h
index 058ccd6..7916534 100644
--- a/src/traced/probes/power/linux_power_sysfs_data_source.h
+++ b/src/traced/probes/power/linux_power_sysfs_data_source.h
@@ -17,7 +17,8 @@
 #ifndef SRC_TRACED_PROBES_POWER_LINUX_POWER_SYSFS_DATA_SOURCE_H_
 #define SRC_TRACED_PROBES_POWER_LINUX_POWER_SYSFS_DATA_SOURCE_H_
 
-#include "perfetto/ext/base/optional.h"
+#include <optional>
+
 #include "perfetto/ext/base/weak_ptr.h"
 #include "perfetto/tracing/core/data_source_config.h"
 #include "src/traced/probes/probes_data_source.h"
@@ -39,22 +40,22 @@
     ~BatteryInfo();
 
     // The current coloumb counter value in µAh.
-    base::Optional<int64_t> GetChargeCounterUah(size_t battery_idx);
+    std::optional<int64_t> GetChargeCounterUah(size_t battery_idx);
 
     // The current energy counter in µWh.
-    base::Optional<int64_t> GetEnergyCounterUah(size_t battery_idx);
+    std::optional<int64_t> GetEnergyCounterUah(size_t battery_idx);
 
     // The voltage in µV.
-    base::Optional<int64_t> GetVoltageUv(size_t battery_idx);
+    std::optional<int64_t> GetVoltageUv(size_t battery_idx);
 
     // The battery capacity in percent.
-    base::Optional<int64_t> GetCapacityPercent(size_t battery_idx);
+    std::optional<int64_t> GetCapacityPercent(size_t battery_idx);
 
     // The current reading of the battery in µA.
-    base::Optional<int64_t> GetCurrentNowUa(size_t battery_idx);
+    std::optional<int64_t> GetCurrentNowUa(size_t battery_idx);
 
     // The smoothed current reading of the battery in µA.
-    base::Optional<int64_t> GetAverageCurrentUa(size_t battery_idx);
+    std::optional<int64_t> GetAverageCurrentUa(size_t battery_idx);
 
     // Name of the battery.
     std::string GetBatteryName(size_t battery_idx);
diff --git a/src/traced/probes/power/linux_power_sysfs_data_source_unittest.cc b/src/traced/probes/power/linux_power_sysfs_data_source_unittest.cc
index 2c1c8aa..442d767 100644
--- a/src/traced/probes/power/linux_power_sysfs_data_source_unittest.cc
+++ b/src/traced/probes/power/linux_power_sysfs_data_source_unittest.cc
@@ -59,9 +59,9 @@
 
   EXPECT_EQ(battery_info_->num_batteries(), 1u);
   EXPECT_EQ(*battery_info_->GetCapacityPercent(0), 88);
-  EXPECT_EQ(battery_info_->GetCurrentNowUa(0), base::nullopt);
-  EXPECT_EQ(battery_info_->GetAverageCurrentUa(0), base::nullopt);
-  EXPECT_EQ(battery_info_->GetChargeCounterUah(0), base::nullopt);
+  EXPECT_EQ(battery_info_->GetCurrentNowUa(0), std::nullopt);
+  EXPECT_EQ(battery_info_->GetAverageCurrentUa(0), std::nullopt);
+  EXPECT_EQ(battery_info_->GetChargeCounterUah(0), std::nullopt);
 }
 
 TEST(LinuxPowerSysfsDataSourceTest, MultipleBatteries) {
@@ -92,11 +92,11 @@
   size_t second_battery_idx = main_battery_idx == 0 ? 1 : 0;
 
   EXPECT_EQ(*battery_info_->GetCapacityPercent(second_battery_idx), 88);
-  EXPECT_EQ(battery_info_->GetCurrentNowUa(second_battery_idx), base::nullopt);
+  EXPECT_EQ(battery_info_->GetCurrentNowUa(second_battery_idx), std::nullopt);
   EXPECT_EQ(battery_info_->GetAverageCurrentUa(second_battery_idx),
-            base::nullopt);
+            std::nullopt);
   EXPECT_EQ(battery_info_->GetChargeCounterUah(second_battery_idx),
-            base::nullopt);
+            std::nullopt);
 
   EXPECT_EQ(*battery_info_->GetCapacityPercent(main_battery_idx), 95);
   EXPECT_EQ(*battery_info_->GetCurrentNowUa(main_battery_idx), 245000);
diff --git a/src/traced/probes/statsd_client/statsd_binder_data_source.cc b/src/traced/probes/statsd_client/statsd_binder_data_source.cc
index b05451b..9b79fca 100644
--- a/src/traced/probes/statsd_client/statsd_binder_data_source.cc
+++ b/src/traced/probes/statsd_client/statsd_binder_data_source.cc
@@ -20,10 +20,10 @@
 
 #include <map>
 #include <mutex>
+#include <optional>
 
 #include "perfetto/base/time.h"
 #include "perfetto/ext/base/no_destructor.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/protozero/scattered_heap_buffer.h"
 #include "perfetto/tracing/core/data_source_config.h"
diff --git a/src/traced/probes/sys_stats/sys_stats_data_source.cc b/src/traced/probes/sys_stats/sys_stats_data_source.cc
index 9543d7c..e5c02a9 100644
--- a/src/traced/probes/sys_stats/sys_stats_data_source.cc
+++ b/src/traced/probes/sys_stats/sys_stats_data_source.cc
@@ -253,7 +253,7 @@
       if (index == 2) {  // index for device name (string)
         disk_stat->set_device_name(words.cur_token());
       } else if (index >= 5) {  // integer values from index 5
-        base::Optional<uint64_t> value_address =
+        std::optional<uint64_t> value_address =
             base::CStringToUInt64(words.cur_token());
         uint64_t value = value_address ? *value_address : 0;
 
diff --git a/src/tracing/console_interceptor.cc b/src/tracing/console_interceptor.cc
index bb700f1..2c3221f 100644
--- a/src/tracing/console_interceptor.cc
+++ b/src/tracing/console_interceptor.cc
@@ -20,11 +20,11 @@
 
 #include <algorithm>
 #include <cmath>
+#include <optional>
 #include <tuple>
 
 #include "perfetto/ext/base/file_utils.h"
 #include "perfetto/ext/base/hash.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/utils.h"
@@ -129,7 +129,7 @@
   using SelfHandle = LockedHandle<ConsoleInterceptor>;
 
   InterceptorContext& context_;
-  base::Optional<SelfHandle> locked_self_;
+  std::optional<SelfHandle> locked_self_;
 };
 
 ConsoleInterceptor::~ConsoleInterceptor() = default;
@@ -157,7 +157,7 @@
   if (locked_self_.has_value())
     return &locked_self_.value()->session_state_;
   locked_self_ =
-      base::make_optional<SelfHandle>(context_.GetInterceptorLocked());
+      std::make_optional<SelfHandle>(context_.GetInterceptorLocked());
   return &locked_self_.value()->session_state_;
 }
 
diff --git a/src/tracing/core/tracing_service_impl.cc b/src/tracing/core/tracing_service_impl.cc
index 0ee3122..8299500 100644
--- a/src/tracing/core/tracing_service_impl.cc
+++ b/src/tracing/core/tracing_service_impl.cc
@@ -1902,7 +1902,7 @@
       // reset to 0 by the service when the chunk was freed).
 
       WriterID writer_id = chunk.writer_id();
-      base::Optional<BufferID> target_buffer_id =
+      std::optional<BufferID> target_buffer_id =
           producer->buffer_id_for_writer(writer_id);
 
       // We can only scrape this chunk if we know which log buffer to copy it
@@ -2833,7 +2833,7 @@
 
   // If the writer was registered by the producer, it should only write into the
   // buffer it was registered with.
-  base::Optional<BufferID> associated_buffer =
+  std::optional<BufferID> associated_buffer =
       producer->buffer_id_for_writer(writer_id);
   if (associated_buffer && *associated_buffer != buffer_id) {
     PERFETTO_ELOG("Writer %" PRIu16 " of producer %" PRIu16
@@ -3334,7 +3334,7 @@
   }
 
   std::string sdk_str_value = base::GetAndroidProp("ro.build.version.sdk");
-  base::Optional<uint64_t> sdk_value = base::StringToUInt64(sdk_str_value);
+  std::optional<uint64_t> sdk_value = base::StringToUInt64(sdk_str_value);
   if (sdk_value.has_value()) {
     info->set_android_sdk_version(*sdk_value);
   } else {
diff --git a/src/tracing/core/tracing_service_impl.h b/src/tracing/core/tracing_service_impl.h
index 02e5639..7cefb9b 100644
--- a/src/tracing/core/tracing_service_impl.h
+++ b/src/tracing/core/tracing_service_impl.h
@@ -22,6 +22,7 @@
 #include <map>
 #include <memory>
 #include <mutex>
+#include <optional>
 #include <random>
 #include <set>
 #include <utility>
@@ -31,7 +32,6 @@
 #include "perfetto/base/status.h"
 #include "perfetto/base/time.h"
 #include "perfetto/ext/base/circular_queue.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/periodic_task.h"
 #include "perfetto/ext/base/uuid.h"
 #include "perfetto/ext/base/weak_ptr.h"
@@ -143,11 +143,11 @@
       return allowed_target_buffers_.count(buffer_id);
     }
 
-    base::Optional<BufferID> buffer_id_for_writer(WriterID writer_id) const {
+    std::optional<BufferID> buffer_id_for_writer(WriterID writer_id) const {
       const auto it = writers_.find(writer_id);
       if (it != writers_.end())
         return it->second;
-      return base::nullopt;
+      return std::nullopt;
     }
 
     uid_t uid() const { return uid_; }
diff --git a/src/tracing/internal/tracing_muxer_impl_integrationtest.cc b/src/tracing/internal/tracing_muxer_impl_integrationtest.cc
index e1cfbfd..5711d73 100644
--- a/src/tracing/internal/tracing_muxer_impl_integrationtest.cc
+++ b/src/tracing/internal/tracing_muxer_impl_integrationtest.cc
@@ -1,8 +1,8 @@
 #include "perfetto/tracing/tracing.h"
 
 #include <stdio.h>
+#include <optional>
 
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/thread_task_runner.h"
 #include "perfetto/ext/base/waitable_event.h"
 #include "perfetto/ext/tracing/ipc/service_ipc_host.h"
@@ -53,7 +53,7 @@
 
   struct EnvVar {
     const char* name;
-    base::Optional<std::string> value;
+    std::optional<std::string> value;
   };
   // Stores previous values of environment variables overridden by tests. We
   // need to to this because some android integration tests need to talk to the
diff --git a/test/end_to_end_shared_memory_fuzzer.cc b/test/end_to_end_shared_memory_fuzzer.cc
index 91f3629..adde520 100644
--- a/test/end_to_end_shared_memory_fuzzer.cc
+++ b/test/end_to_end_shared_memory_fuzzer.cc
@@ -132,7 +132,7 @@
   }
 
  private:
-  base::Optional<base::ThreadTaskRunner> runner_;  // Keep first.
+  std::optional<base::ThreadTaskRunner> runner_;  // Keep first.
 
   std::unique_ptr<FakeProducer> producer_;
   const uint8_t* data_;
diff --git a/test/ftrace_integrationtest.cc b/test/ftrace_integrationtest.cc
index ab86dd5..318b308 100644
--- a/test/ftrace_integrationtest.cc
+++ b/test/ftrace_integrationtest.cc
@@ -319,7 +319,7 @@
   const auto& packets = helper.trace();
   ASSERT_GT(packets.size(), 0u);
 
-  base::Optional<protos::gen::FtraceStats> stats;
+  std::optional<protos::gen::FtraceStats> stats;
   for (const auto& packet : packets) {
     if (!packet.has_ftrace_stats() ||
         packet.ftrace_stats().phase() !=
diff --git a/test/test_helper.h b/test/test_helper.h
index c8d1b48..dd18b4c 100644
--- a/test/test_helper.h
+++ b/test/test_helper.h
@@ -19,10 +19,10 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <optional>
 
 #include "perfetto/base/build_config.h"
 #include "perfetto/ext/base/file_utils.h"
-#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/subprocess.h"
 #include "perfetto/ext/base/thread_task_runner.h"
@@ -109,7 +109,7 @@
  private:
   struct Var {
     const char* name;
-    base::Optional<std::string> value;
+    std::optional<std::string> value;
   };
   std::vector<Var> prev_state_;
 };
@@ -159,7 +159,7 @@
   base::ThreadTaskRunner* runner() { return runner_ ? &*runner_ : nullptr; }
 
  private:
-  base::Optional<base::ThreadTaskRunner> runner_;  // Keep first.
+  std::optional<base::ThreadTaskRunner> runner_;  // Keep first.
 
   std::string producer_socket_;
   std::string consumer_socket_;
@@ -196,7 +196,7 @@
   }
 
  private:
-  base::Optional<base::ThreadTaskRunner> runner_;  // Keep first.
+  std::optional<base::ThreadTaskRunner> runner_;  // Keep first.
 
   std::string producer_socket_;
   std::unique_ptr<ProbesProducer> producer_;
@@ -253,7 +253,7 @@
   }
 
  private:
-  base::Optional<base::ThreadTaskRunner> runner_;  // Keep first.
+  std::optional<base::ThreadTaskRunner> runner_;  // Keep first.
 
   std::string producer_socket_;
   std::unique_ptr<FakeProducer> producer_;