Merge "Move ReadSctpMessage to a class"
diff --git a/aos/aos_cli_utils.cc b/aos/aos_cli_utils.cc
index e36c3d2..84ba1e4 100644
--- a/aos/aos_cli_utils.cc
+++ b/aos/aos_cli_utils.cc
@@ -32,7 +32,8 @@
 
 bool CliUtilInfo::Initialize(
     int *argc, char ***argv,
-    std::function<bool(const aos::Channel *)> channel_filter) {
+    std::function<bool(const aos::Channel *)> channel_filter,
+    bool expect_args) {
   // Don't generate failure output if the config doesn't exist while attempting
   // to autocomplete.
   if (struct stat file_stat;
@@ -44,66 +45,76 @@
     return true;
   }
 
-  std::string channel_name;
-  std::string message_type;
-  if (*argc > 1) {
-    channel_name = (*argv)[1];
-    ShiftArgs(argc, argv);
-  }
-  if (*argc > 1) {
-    message_type = (*argv)[1];
-    ShiftArgs(argc, argv);
-  }
-
   config.emplace(aos::configuration::ReadConfig(FLAGS_config));
   event_loop.emplace(&config->message());
   event_loop->SkipTimingReport();
   event_loop->SkipAosLog();
 
-  if (FLAGS__bash_autocomplete) {
-    Autocomplete(channel_name, message_type, channel_filter);
-    return true;
-  }
-
-  const flatbuffers::Vector<flatbuffers::Offset<aos::Channel>> *const channels =
+  const flatbuffers::Vector<flatbuffers::Offset<aos::Channel>> *channels =
       event_loop->configuration()->channels();
-  if (channel_name.empty() && message_type.empty()) {
-    std::cout << "Channels:\n";
+
+  do {
+    std::string channel_name;
+    std::string message_type;
+    if (*argc > 1) {
+      channel_name = (*argv)[1];
+      ShiftArgs(argc, argv);
+    }
+    if (*argc > 1) {
+      message_type = (*argv)[1];
+      ShiftArgs(argc, argv);
+    }
+
+    if (FLAGS__bash_autocomplete) {
+      Autocomplete(channel_name, message_type, channel_filter);
+      return true;
+    }
+
+    if (channel_name.empty() && message_type.empty()) {
+      std::cout << "Channels:\n";
+      for (const aos::Channel *channel : *channels) {
+        if (FLAGS_all || channel_filter(channel)) {
+          std::cout << channel->name()->c_str() << ' '
+                    << channel->type()->c_str() << '\n';
+        }
+      }
+      return true;
+    }
+
+    std::vector<const aos::Channel *> found_channels_now;
+    bool found_exact = false;
     for (const aos::Channel *channel : *channels) {
-      if (FLAGS_all || channel_filter(channel)) {
-        std::cout << channel->name()->c_str() << ' ' << channel->type()->c_str()
-                  << '\n';
+      if (!FLAGS_all && !channel_filter(channel)) {
+        continue;
       }
-    }
-    return true;
-  }
-
-  bool found_exact = false;
-  for (const aos::Channel *channel : *channels) {
-    if (!FLAGS_all && !channel_filter(channel)) {
-      continue;
-    }
-    if (channel->name()->c_str() != channel_name) {
-      continue;
-    }
-    if (channel->type()->string_view() == message_type) {
-      if (!found_exact) {
-        found_channels.clear();
-        found_exact = true;
+      if (channel->name()->c_str() != channel_name) {
+        continue;
       }
-    } else if (!found_exact && channel->type()->string_view().find(
-                                   message_type) != std::string_view::npos) {
-    } else {
-      continue;
+      if (channel->type()->string_view() == message_type) {
+        if (!found_exact) {
+          found_channels_now.clear();
+          found_exact = true;
+        }
+      } else if (!found_exact && channel->type()->string_view().find(
+                                     message_type) != std::string_view::npos) {
+      } else {
+        continue;
+      }
+      found_channels_now.push_back(channel);
     }
-    found_channels.push_back(channel);
-  }
 
-  if (found_channels.empty()) {
-    LOG(FATAL) << "Could not find any channels with the given name and type.";
-  } else if (found_channels.size() > 1 && !message_type.empty()) {
-    LOG(FATAL) << "Multiple channels found with same type";
-  }
+    if (found_channels_now.empty()) {
+      LOG(FATAL)
+          << "Could not find any channels with the given name and type for "
+          << channel_name << " " << message_type;
+    } else if (found_channels_now.size() > 1 && !message_type.empty()) {
+      LOG(FATAL) << "Multiple channels found with same type for "
+                 << channel_name << " " << message_type;
+    }
+    for (const aos::Channel *channel : found_channels_now) {
+      found_channels.push_back(channel);
+    }
+  } while (expect_args && *argc > 1);
 
   return false;
 }
diff --git a/aos/aos_cli_utils.h b/aos/aos_cli_utils.h
index e158737..962e0a0 100644
--- a/aos/aos_cli_utils.h
+++ b/aos/aos_cli_utils.h
@@ -13,7 +13,8 @@
   // If this returns false, the other fields will be filled out appropriately.
   // event_loop will be filled out before channel_filter is called.
   bool Initialize(int *argc, char ***argv,
-                  std::function<bool(const aos::Channel *)> channel_filter);
+                  std::function<bool(const aos::Channel *)> channel_filter,
+                  bool expect_args);
 
   std::optional<aos::FlatbufferDetachedBuffer<aos::Configuration>> config;
   std::optional<aos::ShmEventLoop> event_loop;
diff --git a/aos/aos_dump.cc b/aos/aos_dump.cc
index e2467b3..477b6fb 100644
--- a/aos/aos_dump.cc
+++ b/aos/aos_dump.cc
@@ -70,11 +70,13 @@
   aos::InitGoogle(&argc, &argv);
 
   aos::CliUtilInfo cli_info;
-  if (cli_info.Initialize(&argc, &argv,
-                          [&cli_info](const aos::Channel *channel) {
-                            return aos::configuration::ChannelIsReadableOnNode(
-                                channel, cli_info.event_loop->node());
-                          })) {
+  if (cli_info.Initialize(
+          &argc, &argv,
+          [&cli_info](const aos::Channel *channel) {
+            return aos::configuration::ChannelIsReadableOnNode(
+                channel, cli_info.event_loop->node());
+          },
+          true)) {
     return 0;
   }
 
diff --git a/aos/aos_send.cc b/aos/aos_send.cc
index d94418a..91ffb1c 100644
--- a/aos/aos_send.cc
+++ b/aos/aos_send.cc
@@ -19,11 +19,13 @@
   aos::InitGoogle(&argc, &argv);
 
   aos::CliUtilInfo cli_info;
-  if (cli_info.Initialize(&argc, &argv,
-                          [&cli_info](const aos::Channel *channel) {
-                            return aos::configuration::ChannelIsSendableOnNode(
-                                channel, cli_info.event_loop->node());
-                          })) {
+  if (cli_info.Initialize(
+          &argc, &argv,
+          [&cli_info](const aos::Channel *channel) {
+            return aos::configuration::ChannelIsSendableOnNode(
+                channel, cli_info.event_loop->node());
+          },
+          false)) {
     return 0;
   }
   if (cli_info.found_channels.size() > 1) {
diff --git a/aos/realtime.cc b/aos/realtime.cc
index cd9ea43..dc53a37 100644
--- a/aos/realtime.cc
+++ b/aos/realtime.cc
@@ -36,6 +36,12 @@
 int MallocHook_AddDeleteHook(MallocHook_DeleteHook hook) __attribute__((weak));
 int MallocHook_RemoveDeleteHook(MallocHook_DeleteHook hook)
     __attribute__((weak));
+
+// Declare tc_malloc weak so we can check if it exists.
+void *tc_malloc(size_t size) __attribute__((weak));
+
+void *__libc_malloc(size_t size);
+void __libc_free(void *ptr);
 }  // extern "C"
 
 namespace FLAG__namespace_do_not_use_directly_use_DECLARE_double_instead {
@@ -251,17 +257,57 @@
   }
 }
 
+extern "C" {
+
+// malloc hooks for libc. Tcmalloc will replace everything it finds (malloc,
+// __libc_malloc, etc.), so we need its specific hook above as well.
+void *aos_malloc_hook(size_t size) {
+  if (FLAGS_die_on_malloc && aos::is_realtime) {
+    aos::is_realtime = false;
+    RAW_LOG(FATAL, "Malloced %zu bytes", size);
+    return nullptr;
+  } else {
+    return __libc_malloc(size);
+  }
+}
+
+void aos_free_hook(void *ptr) {
+  if (FLAGS_die_on_malloc && aos::is_realtime && ptr != nullptr) {
+    aos::is_realtime = false;
+    RAW_LOG(FATAL, "Deleted %p", ptr);
+  } else {
+    __libc_free(ptr);
+  }
+}
+
+void *malloc(size_t size) __attribute__((weak, alias("aos_malloc_hook")));
+void free(void *ptr) __attribute__((weak, alias("aos_free_hook")));
+
+}
+
 void RegisterMallocHook() {
   if (FLAGS_die_on_malloc) {
-    if (&MallocHook_AddNewHook != nullptr) {
-      CHECK(MallocHook_AddNewHook(&NewHook));
+    // tcmalloc redefines __libc_malloc, so use this as a feature test.
+    if (&__libc_malloc == &tc_malloc) {
+      RAW_LOG(INFO, "Hooking tcmalloc for die_on_malloc");
+      if (&MallocHook_AddNewHook != nullptr) {
+        CHECK(MallocHook_AddNewHook(&NewHook));
+      } else {
+        has_malloc_hook = false;
+      }
+      if (&MallocHook_AddDeleteHook != nullptr) {
+        CHECK(MallocHook_AddDeleteHook(&DeleteHook));
+      } else {
+        has_malloc_hook = false;
+      }
     } else {
-      has_malloc_hook = false;
-    }
-    if (&MallocHook_AddDeleteHook != nullptr) {
-      CHECK(MallocHook_AddDeleteHook(&DeleteHook));
-    } else {
-      has_malloc_hook = false;
+      RAW_LOG(INFO, "Replacing glibc malloc");
+      if (&malloc != &aos_malloc_hook) {
+        has_malloc_hook = false;
+      }
+      if (&free != &aos_free_hook) {
+        has_malloc_hook = false;
+      }
     }
   }
 }