Retrieve StackFrameInfo fields from the runtime

Bug: 191862780
Test: atest art_standalone_runtime_tests
Test: atest CtsLibcoreOjTestCases:test.java.lang.StackWalker.Basic
Change-Id: I481e07e0ee23b82ba685478a1b8f27b0747baf31
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 8e77b43..d58c37a 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -209,6 +209,7 @@
         "mirror/method_handles_lookup.cc",
         "mirror/method_type.cc",
         "mirror/object.cc",
+        "mirror/stack_frame_info.cc",
         "mirror/stack_trace_element.cc",
         "mirror/string.cc",
         "mirror/throwable.cc",
@@ -225,6 +226,7 @@
         "native/dalvik_system_ZygoteHooks.cc",
         "native/java_lang_Class.cc",
         "native/java_lang_Object.cc",
+        "native/java_lang_StackStreamFactory.cc",
         "native/java_lang_String.cc",
         "native/java_lang_StringFactory.cc",
         "native/java_lang_System.cc",
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 0e7dee8..666f86e 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -52,6 +52,7 @@
 #include "mirror/object_array-inl.h"
 #include "mirror/proxy.h"
 #include "mirror/reference.h"
+#include "mirror/stack_frame_info.h"
 #include "mirror/stack_trace_element.h"
 #include "mirror/string-inl.h"
 #include "mirror/var_handle.h"
@@ -641,6 +642,20 @@
   }
 };
 
+struct StackFrameInfoOffsets : public CheckOffsets<mirror::StackFrameInfo> {
+  StackFrameInfoOffsets() : CheckOffsets<mirror::StackFrameInfo>(
+      false, "Ljava/lang/StackFrameInfo;") {
+    addOffset(OFFSETOF_MEMBER(mirror::StackFrameInfo, bci_), "bci");
+    addOffset(OFFSETOF_MEMBER(mirror::StackFrameInfo, declaring_class_), "declaringClass");
+    addOffset(OFFSETOF_MEMBER(mirror::StackFrameInfo, file_name_), "fileName");
+    addOffset(OFFSETOF_MEMBER(mirror::StackFrameInfo, line_number_), "lineNumber");
+    addOffset(OFFSETOF_MEMBER(mirror::StackFrameInfo, method_name_), "methodName");
+    addOffset(OFFSETOF_MEMBER(mirror::StackFrameInfo, method_type_), "methodType");
+    addOffset(OFFSETOF_MEMBER(mirror::StackFrameInfo, retain_class_ref_), "retainClassRef");
+    addOffset(OFFSETOF_MEMBER(mirror::StackFrameInfo, ste_), "ste");
+  }
+};
+
 struct ClassLoaderOffsets : public CheckOffsets<mirror::ClassLoader> {
   ClassLoaderOffsets() : CheckOffsets<mirror::ClassLoader>(false, "Ljava/lang/ClassLoader;") {
     addOffset(OFFSETOF_MEMBER(mirror::ClassLoader, allocator_), "allocator");
@@ -859,6 +874,7 @@
   EXPECT_TRUE(ArrayElementVarHandleOffsets().Check());
   EXPECT_TRUE(ByteArrayViewVarHandleOffsets().Check());
   EXPECT_TRUE(ByteBufferViewVarHandleOffsets().Check());
+  EXPECT_TRUE(StackFrameInfoOffsets().Check());
 }
 
 TEST_F(ClassLinkerTest, FindClassNonexistent) {
diff --git a/runtime/mirror/object_reference.h b/runtime/mirror/object_reference.h
index 386244d..4c3a5dc 100644
--- a/runtime/mirror/object_reference.h
+++ b/runtime/mirror/object_reference.h
@@ -50,6 +50,7 @@
     vis("Ljava/lang/ClassNotFoundException;")         \
     vis("Ljava/lang/DexCache;")                       \
     vis("Ljava/lang/Object;")                         \
+    vis("Ljava/lang/StackFrameInfo;")                 \
     vis("Ljava/lang/StackTraceElement;")              \
     vis("Ljava/lang/String;")                         \
     vis("Ljava/lang/Throwable;")                      \
diff --git a/runtime/mirror/stack_frame_info.cc b/runtime/mirror/stack_frame_info.cc
new file mode 100644
index 0000000..dd3e8f7
--- /dev/null
+++ b/runtime/mirror/stack_frame_info.cc
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include "stack_frame_info.h"
+
+#include "class-alloc-inl.h"
+#include "class.h"
+#include "class_root-inl.h"
+#include "gc/accounting/card_table-inl.h"
+#include "handle_scope-inl.h"
+#include "object-inl.h"
+#include "string.h"
+
+namespace art {
+namespace mirror {
+
+void StackFrameInfo::AssignFields(Handle<Class> declaring_class,
+                                  Handle<MethodType> method_type,
+                                  Handle<String> method_name,
+                                  Handle<String> file_name,
+                                  int32_t line_number,
+                                  int32_t dex_pc) {
+  if (Runtime::Current()->IsActiveTransaction()) {
+    SetFields<true>(declaring_class.Get(), method_type.Get(), method_name.Get(),
+                    file_name.Get(), line_number, dex_pc);
+  } else {
+    SetFields<false>(declaring_class.Get(), method_type.Get(), method_name.Get(),
+                     file_name.Get(), line_number, dex_pc);
+  }
+}
+
+template<bool kTransactionActive>
+void StackFrameInfo::SetFields(ObjPtr<Class> declaring_class,
+                               ObjPtr<MethodType> method_type,
+                               ObjPtr<String> method_name,
+                               ObjPtr<String> file_name,
+                               int32_t line_number,
+                               int32_t bci) {
+  SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackFrameInfo, declaring_class_),
+                                     declaring_class);
+  SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackFrameInfo, method_type_),
+                                     method_type);
+  SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackFrameInfo, method_name_),
+                                     method_name);
+  SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackFrameInfo, file_name_),
+                                     file_name);
+  SetField32<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackFrameInfo, line_number_),
+                                 line_number);
+  SetField32<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackFrameInfo, bci_),
+                                 bci);
+}
+
+}  // namespace mirror
+}  // namespace art
diff --git a/runtime/mirror/stack_frame_info.h b/runtime/mirror/stack_frame_info.h
new file mode 100644
index 0000000..24f8c8f
--- /dev/null
+++ b/runtime/mirror/stack_frame_info.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 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 ART_RUNTIME_MIRROR_STACK_FRAME_INFO_H_
+#define ART_RUNTIME_MIRROR_STACK_FRAME_INFO_H_
+
+#include "method_type.h"
+#include "object.h"
+#include "stack_trace_element.h"
+
+namespace art {
+
+template<class T> class Handle;
+struct StackFrameInfoOffsets;
+
+namespace mirror {
+
+// C++ mirror of java.lang.StackFrameInfo
+class MANAGED StackFrameInfo final : public Object {
+ public:
+  MIRROR_CLASS("Ljava/lang/StackFrameInfo;");
+
+  void AssignFields(Handle<Class> declaring_class,
+                    Handle<MethodType> method_type,
+                    Handle<String> method_name,
+                    Handle<String> file_name,
+                    int32_t line_number,
+                    int32_t dex_pc)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+  // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
+  HeapReference<Class> declaring_class_;
+  HeapReference<String> file_name_;
+  HeapReference<String> method_name_;
+  HeapReference<Class> method_type_;
+  HeapReference<StackTraceElement> ste_;
+  int32_t bci_;
+  int32_t line_number_;
+  bool retain_class_ref_;
+
+  template<bool kTransactionActive>
+  void SetFields(ObjPtr<Class> declaring_class,
+                 ObjPtr<MethodType> method_type,
+                 ObjPtr<String> method_name,
+                 ObjPtr<String> file_name,
+                 int32_t line_number,
+                 int32_t bci)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
+  friend struct art::StackFrameInfoOffsets;  // for verifying offset information
+  DISALLOW_IMPLICIT_CONSTRUCTORS(StackFrameInfo);
+};
+
+}  // namespace mirror
+}  // namespace art
+
+#endif  // ART_RUNTIME_MIRROR_STACK_FRAME_INFO_H_
diff --git a/runtime/native/java_lang_StackStreamFactory.cc b/runtime/native/java_lang_StackStreamFactory.cc
new file mode 100644
index 0000000..b4ad042
--- /dev/null
+++ b/runtime/native/java_lang_StackStreamFactory.cc
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include "java_lang_StackStreamFactory.h"
+
+#include "nativehelper/jni_macros.h"
+
+#include "jni/jni_internal.h"
+#include "native_util.h"
+#include "scoped_fast_native_object_access-inl.h"
+#include "thread.h"
+
+namespace art {
+
+static jobject StackStreamFactory_nativeGetStackAnchor(JNIEnv* env, jclass) {
+  ScopedFastNativeObjectAccess soa(env);
+  return soa.Self()->CreateInternalStackTrace(soa);
+}
+
+static jint StackStreamFactory_nativeFetchStackFrameInfo(JNIEnv* env, jclass,
+    jlong mode, jobject anchor, jint startLevel, jint batchSize, jint startBufferIndex,
+    jobjectArray frameBuffer) {
+  if (anchor == nullptr) {
+      return startLevel;
+  }
+  ScopedFastNativeObjectAccess soa(env);
+  return Thread::InternalStackTraceToStackFrameInfoArray(soa, mode, anchor,
+    startLevel, batchSize, startBufferIndex, frameBuffer);
+}
+
+static JNINativeMethod gMethods[] = {
+  FAST_NATIVE_METHOD(StackStreamFactory, nativeGetStackAnchor, "()Ljava/lang/Object;"),
+  FAST_NATIVE_METHOD(StackStreamFactory, nativeFetchStackFrameInfo, "(JLjava/lang/Object;III[Ljava/lang/StackFrameInfo;)I"),
+};
+
+void register_java_lang_StackStreamFactory(JNIEnv* env) {
+  REGISTER_NATIVE_METHODS("java/lang/StackStreamFactory");
+}
+
+}  // namespace art
diff --git a/runtime/native/java_lang_StackStreamFactory.h b/runtime/native/java_lang_StackStreamFactory.h
new file mode 100644
index 0000000..2216871
--- /dev/null
+++ b/runtime/native/java_lang_StackStreamFactory.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2022 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 ART_RUNTIME_NATIVE_JAVA_LANG_STACKSTREAMFACTORY_H_
+#define ART_RUNTIME_NATIVE_JAVA_LANG_STACKSTREAMFACTORY_H_
+
+#include <jni.h>
+
+namespace art {
+
+void register_java_lang_StackStreamFactory(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_JAVA_LANG_STACKSTREAMFACTORY_H_
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 3645695..774a3cf 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -125,6 +125,7 @@
 #include "native/dalvik_system_ZygoteHooks.h"
 #include "native/java_lang_Class.h"
 #include "native/java_lang_Object.h"
+#include "native/java_lang_StackStreamFactory.h"
 #include "native/java_lang_String.h"
 #include "native/java_lang_StringFactory.h"
 #include "native/java_lang_System.h"
@@ -2259,6 +2260,7 @@
   register_java_lang_reflect_Parameter(env);
   register_java_lang_reflect_Proxy(env);
   register_java_lang_ref_Reference(env);
+  register_java_lang_StackStreamFactory(env);
   register_java_lang_String(env);
   register_java_lang_StringFactory(env);
   register_java_lang_System(env);
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 87fa16d..618360c 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -86,6 +86,7 @@
 #include "mirror/class_loader.h"
 #include "mirror/object_array-alloc-inl.h"
 #include "mirror/object_array-inl.h"
+#include "mirror/stack_frame_info.h"
 #include "mirror/stack_trace_element.h"
 #include "monitor.h"
 #include "monitor_objects_stack_visitor.h"
@@ -3156,6 +3157,135 @@
   return result;
 }
 
+static ObjPtr<mirror::StackFrameInfo> InitStackFrameInfo(
+    const ScopedObjectAccessAlreadyRunnable& soa,
+    ClassLinker* class_linker,
+    ObjPtr<mirror::StackFrameInfo> stackFrameInfo,
+    ArtMethod* method,
+    uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_) {
+  StackHandleScope<4> hs(soa.Self());
+  int32_t line_number;
+  auto source_name_object(hs.NewHandle<mirror::String>(nullptr));
+  if (method->IsProxyMethod()) {
+    line_number = -1;
+    // source_name_object intentionally left null for proxy methods
+  } else {
+    line_number = method->GetLineNumFromDexPC(dex_pc);
+    if (line_number == -1) {
+      // Make the line_number field of StackFrameInfo hold the dex pc.
+      // source_name_object is intentionally left null if we failed to map the dex pc to
+      // a line number (most probably because there is no debug info). See b/30183883.
+      line_number = static_cast<int32_t>(dex_pc);
+    } else {
+      const char* source_file = method->GetDeclaringClassSourceFile();
+      if (source_file != nullptr) {
+        source_name_object.Assign(mirror::String::AllocFromModifiedUtf8(soa.Self(), source_file));
+        if (source_name_object == nullptr) {
+          soa.Self()->AssertPendingOOMException();
+          return nullptr;
+        }
+      }
+    }
+  }
+
+  auto declaring_class_object(hs.NewHandle<mirror::Class>(method->GetDeclaringClass()));
+
+  ArtMethod* interface_method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize);
+  const char* method_name = interface_method->GetName();
+  CHECK(method_name != nullptr);
+  auto method_name_object(
+      hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), method_name)));
+  if (method_name_object == nullptr) {
+    soa.Self()->AssertPendingOOMException();
+    return nullptr;
+  }
+
+  dex::ProtoIndex proto_idx =
+      method->GetDexFile()->GetIndexForProtoId(interface_method->GetPrototype());
+  auto method_type_object(hs.NewHandle<mirror::MethodType>(
+      class_linker->ResolveMethodType(soa.Self(), proto_idx, interface_method)));
+  if (method_type_object == nullptr) {
+    soa.Self()->AssertPendingOOMException();
+    return nullptr;
+  }
+
+  stackFrameInfo->AssignFields(declaring_class_object,
+                               method_type_object,
+                               method_name_object,
+                               source_name_object,
+                               line_number,
+                               static_cast<int32_t>(dex_pc));
+  return stackFrameInfo;
+}
+
+jint Thread::InternalStackTraceToStackFrameInfoArray(
+    const ScopedObjectAccessAlreadyRunnable& soa,
+    [[maybe_unused]] jlong mode,  // See java.lang.StackStreamFactory for the mode flags
+    jobject internal,
+    jint startLevel,
+    jint batchSize,
+    jint startBufferIndex,
+    jobjectArray output_array) {
+  // Decode the internal stack trace into the depth, method trace and PC trace.
+  // Subtract one for the methods and PC trace.
+  int32_t depth = soa.Decode<mirror::Array>(internal)->GetLength() - 1;
+  DCHECK_GE(depth, 0);
+
+  ObjPtr<mirror::ObjectArray<mirror::Object>> frames =
+      soa.Decode<mirror::ObjectArray<mirror::Object>>(output_array);
+
+  jint endBufferIndex = startBufferIndex;
+
+  if (startLevel < 0 || startLevel >= depth) {
+    return endBufferIndex;
+  }
+
+  int32_t bufferSize = frames->GetLength();
+  if (startBufferIndex < 0 || startBufferIndex >= bufferSize) {
+    return endBufferIndex;
+  }
+
+  ObjPtr<mirror::ObjectArray<mirror::Object>> decoded_traces =
+      soa.Decode<mirror::Object>(internal)->AsObjectArray<mirror::Object>();
+  // Methods and dex PC trace is element 0.
+  DCHECK(decoded_traces->Get(0)->IsIntArray() || decoded_traces->Get(0)->IsLongArray());
+  const ObjPtr<mirror::PointerArray> method_trace =
+      ObjPtr<mirror::PointerArray>::DownCast(decoded_traces->Get(0));
+
+  ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+  StackHandleScope<1> hs(soa.Self());
+  Handle<mirror::Class> sfi_class =
+      hs.NewHandle(class_linker->FindSystemClass(soa.Self(), "Ljava/lang/StackFrameInfo;"));
+  DCHECK(sfi_class != nullptr);
+
+  for (uint32_t i = static_cast<uint32_t>(startLevel); i < static_cast<uint32_t>(depth); ++i) {
+    if (endBufferIndex >= startBufferIndex + batchSize || endBufferIndex >= bufferSize) {
+      break;
+    }
+
+    // Prepare parameters for fields in StackFrameInfo
+    ArtMethod* method = method_trace->GetElementPtrSize<ArtMethod*>(i, kRuntimePointerSize);
+    uint32_t dex_pc = method_trace->GetElementPtrSize<uint32_t>(
+        i + static_cast<uint32_t>(method_trace->GetLength()) / 2, kRuntimePointerSize);
+
+    ObjPtr<mirror::Object> frameObject = frames->Get(endBufferIndex);
+    // If libcore didn't allocate the object, we just stop here, but it's unlikely.
+    if (frameObject == nullptr || !frameObject->InstanceOf(sfi_class.Get())) {
+      break;
+    }
+    auto frame = ObjPtr<mirror::StackFrameInfo>::DownCast(frameObject);
+    frame = InitStackFrameInfo(soa, class_linker, frame, method, dex_pc);
+    // Break if InitStackFrameInfo fails to allocate objects or assign the fields.
+    if (frame == nullptr) {
+      break;
+    }
+
+    ++endBufferIndex;
+  }
+
+  return endBufferIndex;
+}
+
 jobjectArray Thread::CreateAnnotatedStackTrace(const ScopedObjectAccessAlreadyRunnable& soa) const {
   // This code allocates. Do not allow it to operate with a pending exception.
   if (IsExceptionPending()) {
diff --git a/runtime/thread.h b/runtime/thread.h
index c1c7036..7fbeb30 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -718,6 +718,16 @@
       jobjectArray output_array = nullptr, int* stack_depth = nullptr)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  static jint InternalStackTraceToStackFrameInfoArray(
+      const ScopedObjectAccessAlreadyRunnable& soa,
+      jlong mode,  // See java.lang.StackStreamFactory for the mode flags
+      jobject internal,
+      jint startLevel,
+      jint batchSize,
+      jint startIndex,
+      jobjectArray output_array)  // java.lang.StackFrameInfo[]
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
   jobjectArray CreateAnnotatedStackTrace(const ScopedObjectAccessAlreadyRunnable& soa) const
       REQUIRES_SHARED(Locks::mutator_lock_);