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_);