1 /* Copyright (C) 2017 The Android Open Source Project
2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3  *
4  * This file implements interfaces from the file jvmti.h. This implementation
5  * is licensed under the same terms as the file jvmti.h.  The
6  * copyright and license information for the file jvmti.h follows.
7  *
8  * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
9  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
10  *
11  * This code is free software; you can redistribute it and/or modify it
12  * under the terms of the GNU General Public License version 2 only, as
13  * published by the Free Software Foundation.  Oracle designates this
14  * particular file as subject to the "Classpath" exception as provided
15  * by Oracle in the LICENSE file that accompanied this code.
16  *
17  * This code is distributed in the hope that it will be useful, but WITHOUT
18  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20  * version 2 for more details (a copy is included in the LICENSE file that
21  * accompanied this code).
22  *
23  * You should have received a copy of the GNU General Public License version
24  * 2 along with this work; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
26  *
27  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
28  * or visit www.oracle.com if you need additional information or have any
29  * questions.
30  */
31 
32 #include <functional>
33 
34 #include "ti_breakpoint.h"
35 
36 #include "art_jvmti.h"
37 #include "art_method-inl.h"
38 #include "base/enums.h"
39 #include "base/mutex-inl.h"
40 #include "deopt_manager.h"
41 #include "dex/dex_file_annotations.h"
42 #include "dex/modifiers.h"
43 #include "events-inl.h"
44 #include "jni/jni_internal.h"
45 #include "mirror/class-inl.h"
46 #include "mirror/object_array-inl.h"
47 #include "nativehelper/scoped_local_ref.h"
48 #include "runtime_callbacks.h"
49 #include "scoped_thread_state_change-inl.h"
50 #include "thread-current-inl.h"
51 #include "thread_list.h"
52 #include "ti_phase.h"
53 
54 namespace openjdkjvmti {
55 
hash() const56 size_t Breakpoint::hash() const {
57   return std::hash<uintptr_t> {}(reinterpret_cast<uintptr_t>(method_))
58       ^ std::hash<jlocation> {}(location_);
59 }
60 
Breakpoint(art::ArtMethod * m,jlocation loc)61 Breakpoint::Breakpoint(art::ArtMethod* m, jlocation loc) : method_(m), location_(loc) {
62   DCHECK(!m->IsDefault() || !m->IsCopied() || !m->IsInvokable())
63       << "Flags are: 0x" << std::hex << m->GetAccessFlags();
64 }
65 
RemoveBreakpointsInClass(ArtJvmTiEnv * env,art::mirror::Class * klass)66 void BreakpointUtil::RemoveBreakpointsInClass(ArtJvmTiEnv* env, art::mirror::Class* klass) {
67   std::vector<Breakpoint> to_remove;
68   {
69     art::WriterMutexLock lk(art::Thread::Current(), env->event_info_mutex_);
70     for (const Breakpoint& b : env->breakpoints) {
71       if (b.GetMethod()->GetDeclaringClass() == klass) {
72         to_remove.push_back(b);
73       }
74     }
75     for (const Breakpoint& b : to_remove) {
76       auto it = env->breakpoints.find(b);
77       DCHECK(it != env->breakpoints.end());
78       env->breakpoints.erase(it);
79     }
80   }
81   DeoptManager* deopt = DeoptManager::Get();
82   for (const Breakpoint& b : to_remove) {
83     // TODO It might be good to send these all at once instead.
84     deopt->RemoveMethodBreakpoint(b.GetMethod());
85   }
86 }
87 
SetBreakpoint(jvmtiEnv * jenv,jmethodID method,jlocation location)88 jvmtiError BreakpointUtil::SetBreakpoint(jvmtiEnv* jenv, jmethodID method, jlocation location) {
89   ArtJvmTiEnv* env = ArtJvmTiEnv::AsArtJvmTiEnv(jenv);
90   if (method == nullptr) {
91     return ERR(INVALID_METHODID);
92   }
93   art::ScopedObjectAccess soa(art::Thread::Current());
94   art::ArtMethod* art_method = art::jni::DecodeArtMethod(method)->GetCanonicalMethod();
95   if (location < 0 || static_cast<uint32_t>(location) >=
96       art_method->DexInstructions().InsnsSizeInCodeUnits()) {
97     return ERR(INVALID_LOCATION);
98   }
99   DeoptManager::Get()->AddMethodBreakpoint(art_method);
100   {
101     art::WriterMutexLock lk(art::Thread::Current(), env->event_info_mutex_);
102     auto res_pair = env->breakpoints.insert(/* Breakpoint */ {art_method, location});
103     if (LIKELY(res_pair.second)) {
104       return OK;
105     }
106   }
107   // Didn't get inserted because it's already present!
108   DeoptManager::Get()->RemoveMethodBreakpoint(art_method);
109   return ERR(DUPLICATE);
110 }
111 
ClearBreakpoint(jvmtiEnv * jenv,jmethodID method,jlocation location)112 jvmtiError BreakpointUtil::ClearBreakpoint(jvmtiEnv* jenv, jmethodID method, jlocation location) {
113   ArtJvmTiEnv* env = ArtJvmTiEnv::AsArtJvmTiEnv(jenv);
114   if (method == nullptr) {
115     return ERR(INVALID_METHODID);
116   }
117   art::ScopedObjectAccess soa(art::Thread::Current());
118   art::ArtMethod* art_method = art::jni::DecodeArtMethod(method)->GetCanonicalMethod();
119   {
120     art::WriterMutexLock lk(art::Thread::Current(), env->event_info_mutex_);
121     auto pos = env->breakpoints.find(/* Breakpoint */ {art_method, location});
122     if (pos == env->breakpoints.end()) {
123       return ERR(NOT_FOUND);
124     }
125     env->breakpoints.erase(pos);
126   }
127   DeoptManager::Get()->RemoveMethodBreakpoint(art_method);
128   return OK;
129 }
130 
131 }  // namespace openjdkjvmti
132