Add new set_verbose_flag_ext JVMTI Extension

Add a new jvmti extension function
"com.android.art.misc.set_verbose_flag_ext" that enables one to set
(or disable) verbose logging options using strings with the same
format as the '-verbose:' command line flag.

Bug: 144947842
Test: atest CtsJvmtiRunTest1982HostTestCases
Change-Id: Ideb1663f5cacfacaeab8cc180372ef479dfc5829
diff --git a/openjdkjvmti/ti_logging.cc b/openjdkjvmti/ti_logging.cc
index 60f4340..8740ec6 100644
--- a/openjdkjvmti/ti_logging.cc
+++ b/openjdkjvmti/ti_logging.cc
@@ -33,8 +33,11 @@
 
 #include "art_jvmti.h"
 
+#include "base/logging.h"
 #include "base/mutex.h"
 #include "base/strlcpy.h"
+#include "cmdline_types.h"
+#include "jvmti.h"
 #include "thread-current-inl.h"
 
 namespace openjdkjvmti {
@@ -69,4 +72,91 @@
   return OK;
 }
 
+jvmtiError LogUtil::SetVerboseFlagExt(jvmtiEnv* env, const char* data, jboolean enable) {
+  if (env == nullptr) {
+    return ERR(INVALID_ENVIRONMENT);
+  } else if (data == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+  bool new_value = (enable == JNI_TRUE) ? true : false;
+  art::CmdlineType<art::LogVerbosity> cmdline_parser;
+  std::string parse_data(data);
+  art::CmdlineType<art::LogVerbosity>::Result result = cmdline_parser.Parse(parse_data);
+  if (result.IsError()) {
+    JVMTI_LOG(INFO, env) << "Invalid verbose argument: '" << parse_data << "'. Error was "
+                         << result.GetMessage();
+    return ERR(ILLEGAL_ARGUMENT);
+  }
+
+  const art::LogVerbosity& input_verbosity = result.GetValue();
+  const bool* verbosity_arr = reinterpret_cast<const bool*>(&input_verbosity);
+  bool* g_log_verbosity_arr = reinterpret_cast<bool*>(&art::gLogVerbosity);
+  // Copy/invert the verbosity byte-by-byte (sizeof(bool) == 1).
+  for (size_t i = 0; i < sizeof(art::LogVerbosity); i++) {
+    if (verbosity_arr[i]) {
+      g_log_verbosity_arr[i] = new_value;
+    }
+  }
+  return OK;
+}
+
+jvmtiError LogUtil::SetVerboseFlag(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                                   jvmtiVerboseFlag flag,
+                                   jboolean value) {
+  if (flag == jvmtiVerboseFlag::JVMTI_VERBOSE_OTHER) {
+    // OTHER is special, as it's 0, so can't do a bit check.
+    bool val = (value == JNI_TRUE) ? true : false;
+
+    art::gLogVerbosity.collector = val;
+    art::gLogVerbosity.compiler = val;
+    art::gLogVerbosity.deopt = val;
+    art::gLogVerbosity.heap = val;
+    art::gLogVerbosity.interpreter = val;
+    art::gLogVerbosity.jdwp = val;
+    art::gLogVerbosity.jit = val;
+    art::gLogVerbosity.monitor = val;
+    art::gLogVerbosity.oat = val;
+    art::gLogVerbosity.profiler = val;
+    art::gLogVerbosity.signals = val;
+    art::gLogVerbosity.simulator = val;
+    art::gLogVerbosity.startup = val;
+    art::gLogVerbosity.third_party_jni = val;
+    art::gLogVerbosity.threads = val;
+    art::gLogVerbosity.verifier = val;
+    // Do not set verifier-debug.
+    art::gLogVerbosity.image = val;
+    art::gLogVerbosity.plugin = val;
+
+    // Note: can't switch systrace_lock_logging. That requires changing entrypoints.
+
+    art::gLogVerbosity.agents = val;
+  } else {
+    // Spec isn't clear whether "flag" is a mask or supposed to be single. We implement the mask
+    // semantics.
+    constexpr std::underlying_type<jvmtiVerboseFlag>::type kMask =
+        jvmtiVerboseFlag::JVMTI_VERBOSE_GC |
+        jvmtiVerboseFlag::JVMTI_VERBOSE_CLASS |
+        jvmtiVerboseFlag::JVMTI_VERBOSE_JNI;
+    if ((flag & ~kMask) != 0) {
+      return ERR(ILLEGAL_ARGUMENT);
+    }
+
+    bool val = (value == JNI_TRUE) ? true : false;
+
+    if ((flag & jvmtiVerboseFlag::JVMTI_VERBOSE_GC) != 0) {
+      art::gLogVerbosity.gc = val;
+    }
+
+    if ((flag & jvmtiVerboseFlag::JVMTI_VERBOSE_CLASS) != 0) {
+      art::gLogVerbosity.class_linker = val;
+    }
+
+    if ((flag & jvmtiVerboseFlag::JVMTI_VERBOSE_JNI) != 0) {
+      art::gLogVerbosity.jni = val;
+    }
+  }
+
+  return ERR(NONE);
+}
+
 }  // namespace openjdkjvmti