xref: /aosp_15_r20/art/runtime/quick_exception_handler.h (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef ART_RUNTIME_QUICK_EXCEPTION_HANDLER_H_
18 #define ART_RUNTIME_QUICK_EXCEPTION_HANDLER_H_
19 
20 #include <android-base/logging.h>
21 #include <cstdint>
22 #include <memory>
23 #include <optional>
24 
25 #include "base/array_ref.h"
26 #include "base/macros.h"
27 #include "base/mutex.h"
28 #include "deoptimization_kind.h"
29 #include "oat/stack_map.h"
30 #include "stack_reference.h"
31 
32 namespace art HIDDEN {
33 
34 namespace mirror {
35 class Throwable;
36 }  // namespace mirror
37 class ArtMethod;
38 class Context;
39 class OatQuickMethodHeader;
40 class Thread;
41 class ShadowFrame;
42 class StackVisitor;
43 
44 // Manages exception delivery for Quick backend.
45 class QuickExceptionHandler {
46  public:
47   QuickExceptionHandler(Thread* self, bool is_deoptimization)
48       REQUIRES_SHARED(Locks::mutator_lock_);
49 
50   // Find the catch handler for the given exception and call all required Instrumentation methods.
51   // Note this might result in the exception being caught being different from 'exception'.
52   void FindCatch(ObjPtr<mirror::Throwable> exception, bool is_method_exit_exception)
53       REQUIRES_SHARED(Locks::mutator_lock_);
54 
55   // Deoptimize the stack to the upcall/some code that's not deoptimizeable. For
56   // every compiled frame, we create a "copy" shadow frame that will be executed
57   // with the interpreter.
58   // skip_method_exit_callbacks specifies if we should skip method exit callbacks for the top frame.
59   // It is set if a deopt is needed after calling method exit callback for ex: if the callback
60   // throws or performs other actions that require a deopt.
61   void DeoptimizeStack(bool skip_method_exit_callbacks) REQUIRES_SHARED(Locks::mutator_lock_);
62 
63   // Deoptimize a single frame. It's directly triggered from compiled code. It
64   // has the following properties:
65   // - It deoptimizes a single frame, which can include multiple inlined frames.
66   // - It doesn't have return result or pending exception at the deoptimization point.
67   // - It always deoptimizes, even if IsDeoptimizeable() returns false for the
68   //   code, since HDeoptimize always saves the full environment. So it overrides
69   //   the result of IsDeoptimizeable().
70   // - It can be either full-fragment, or partial-fragment deoptimization, depending
71   //   on whether that single frame covers full or partial fragment.
72   void DeoptimizeSingleFrame(DeoptimizationKind kind) REQUIRES_SHARED(Locks::mutator_lock_);
73 
74   void DeoptimizePartialFragmentFixup() REQUIRES_SHARED(Locks::mutator_lock_);
75 
76   // Set up environment before delivering an exception to optimized code.
77   void SetCatchEnvironmentForOptimizedHandler(StackVisitor* stack_visitor)
78       REQUIRES_SHARED(Locks::mutator_lock_);
79 
80   // Prepares a long jump context for a jump to either to a catch handler or to the upcall.
81   std::unique_ptr<Context> PrepareLongJump(bool smash_caller_saves = true)
82       REQUIRES_SHARED(Locks::mutator_lock_);
83 
SetHandlerQuickFrame(ArtMethod ** handler_quick_frame)84   void SetHandlerQuickFrame(ArtMethod** handler_quick_frame) {
85     handler_quick_frame_ = handler_quick_frame;
86   }
87 
SetHandlerQuickFramePc(uintptr_t handler_quick_frame_pc)88   void SetHandlerQuickFramePc(uintptr_t handler_quick_frame_pc) {
89     handler_quick_frame_pc_ = handler_quick_frame_pc;
90   }
91 
SetHandlerMethodHeader(const OatQuickMethodHeader * handler_method_header)92   void SetHandlerMethodHeader(const OatQuickMethodHeader* handler_method_header) {
93     handler_method_header_ = handler_method_header;
94   }
95 
SetHandlerQuickArg0(uintptr_t handler_quick_arg0)96   void SetHandlerQuickArg0(uintptr_t handler_quick_arg0) {
97     handler_quick_arg0_ = handler_quick_arg0;
98   }
99 
GetHandlerMethod()100   ArtMethod* GetHandlerMethod() const {
101     return *handler_quick_frame_;
102   }
103 
GetHandlerDexPcList()104   ArrayRef<const uint32_t> GetHandlerDexPcList() const {
105     DCHECK(handler_dex_pc_list_.has_value());
106     return ArrayRef<const uint32_t>(handler_dex_pc_list_.value());
107   }
108 
SetHandlerDexPcList(std::vector<uint32_t> && handler_dex_pc_list)109   void SetHandlerDexPcList(std::vector<uint32_t>&& handler_dex_pc_list) {
110     handler_dex_pc_list_ = std::move(handler_dex_pc_list);
111   }
112 
GetCatchStackMapRow()113   uint32_t GetCatchStackMapRow() const {
114     return catch_stack_map_row_;
115   }
116 
SetCatchStackMapRow(uint32_t stack_map_row)117   void SetCatchStackMapRow(uint32_t stack_map_row) {
118     catch_stack_map_row_ = stack_map_row;
119   }
120 
GetClearException()121   bool GetClearException() const {
122     return clear_exception_;
123   }
124 
SetClearException(bool clear_exception)125   void SetClearException(bool clear_exception) {
126     clear_exception_ = clear_exception;
127   }
128 
SetHandlerFrameDepth(size_t frame_depth)129   void SetHandlerFrameDepth(size_t frame_depth) {
130     handler_frame_depth_ = frame_depth;
131   }
132 
IsFullFragmentDone()133   bool IsFullFragmentDone() const {
134     return full_fragment_done_;
135   }
136 
SetFullFragmentDone(bool full_fragment_done)137   void SetFullFragmentDone(bool full_fragment_done) {
138     full_fragment_done_ = full_fragment_done;
139   }
140 
141   // Walk the stack frames of the given thread, printing out non-runtime methods with their types
142   // of frames. Helps to verify that partial-fragment deopt really works as expected.
143   static void DumpFramesWithType(Thread* self, bool details = false)
144       REQUIRES_SHARED(Locks::mutator_lock_);
145 
146  private:
147   Thread* const self_;
148   std::unique_ptr<Context> context_;
149   // Should we deoptimize the stack?
150   const bool is_deoptimization_;
151   // Quick frame with found handler or last frame if no handler found.
152   ArtMethod** handler_quick_frame_;
153   // PC to branch to for the handler.
154   uintptr_t handler_quick_frame_pc_;
155   // Quick code of the handler.
156   const OatQuickMethodHeader* handler_method_header_;
157   // The value for argument 0.
158   uintptr_t handler_quick_arg0_;
159   // The handler's dex PC list including the inline dex_pcs. The dex_pcs are ordered from outermost
160   // to innermost. An empty list implies an uncaught exception.
161   // Marked as optional so that we can make sure we destroy it before doing a long jump.
162   std::optional<std::vector<uint32_t>> handler_dex_pc_list_;
163   // StackMap row corresponding to the found catch.
164   uint32_t catch_stack_map_row_;
165   // Should the exception be cleared as the catch block has no move-exception?
166   bool clear_exception_;
167   // Frame depth of the catch handler or the upcall.
168   size_t handler_frame_depth_;
169   // Does the handler successfully walk the full fragment (not stopped
170   // by some code that's not deoptimizeable)? Even single-frame deoptimization
171   // can set this to true if the fragment contains only one quick frame.
172   bool full_fragment_done_;
173 
174   void PrepareForLongJumpToInvokeStubOrInterpreterBridge()
175       REQUIRES_SHARED(Locks::mutator_lock_);
176 
177   DISALLOW_COPY_AND_ASSIGN(QuickExceptionHandler);
178 };
179 
180 }  // namespace art
181 #endif  // ART_RUNTIME_QUICK_EXCEPTION_HANDLER_H_
182