Integrate CallbacksMonitor into Java Borg Debugger for the upcoming s… · pdeva/cloud-debug-java@5e6ef36 · GitHub
Skip to content

Commit 5e6ef36

Browse files
vladlfemrekultursay
authored andcommitted
Integrate CallbacksMonitor into Java Borg Debugger for the upcoming support of breakpoints canary
FEATURE: borg-debugger RELNOTES: N/A ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=112839765
1 parent 9c13699 commit 5e6ef36

6 files changed

Lines changed: 269 additions & 0 deletions

File tree

src/agent/callbacks_monitor.cc

Lines changed: 106 additions & 0 deletions

src/agent/callbacks_monitor.h

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/**
2+
* Copyright 2016 Google Inc. All Rights Reserved.
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 DEVTOOLS_CDBG_COMMON_CALLBACKS_MONITOR_H_
18+
#define DEVTOOLS_CDBG_COMMON_CALLBACKS_MONITOR_H_
19+
20+
#include <functional>
21+
#include <list>
22+
#include <mutex> // NOLINT
23+
24+
#include "common.h"
25+
26+
namespace devtools {
27+
namespace cdbg {
28+
29+
// Keeps track of all the ongoing and past calls to allow detection of
30+
// stuck ones or those that just take too much time. Once a stuck callback is
31+
// detected, the caller should declare the agent as unhealthy.
32+
//
33+
// The class is optimized for performance of registering/completing new calls.
34+
class CallbacksMonitor {
35+
public:
36+
struct OngoingCall {
37+
int64 start_time_ms;
38+
const char* tag;
39+
};
40+
41+
typedef std::list<OngoingCall>::iterator Id;
42+
43+
CallbacksMonitor(
44+
int max_call_duration_ms,
45+
std::function<int64()> fn_gettime = MonotonicClockMillis)
46+
: max_call_duration_ms_(max_call_duration_ms),
47+
fn_gettime_(fn_gettime),
48+
last_unhealthy_time_ms_(-1) {
49+
}
50+
51+
~CallbacksMonitor() {
52+
DCHECK(ongoing_calls_.empty());
53+
}
54+
55+
// One time initialization of the global instance.
56+
static void InitializeSingleton(int max_interval_ms);
57+
58+
// One time cleanup of the global instance.
59+
static void CleanupSingleton();
60+
61+
// Gets the global instance of this class.
62+
static CallbacksMonitor* GetInstance();
63+
64+
// Gets the current time.
65+
int64 GetCurrentTimeMillis() const { return fn_gettime_(); }
66+
67+
// Notifies start of an operation to monitor. The "tag" is a human readable
68+
// name of this callback. It is only used for logging purposes.
69+
Id RegisterCall(const char* tag);
70+
71+
// Notifies completion of an operation started with "RegisterCall".
72+
void CompleteCall(Id id);
73+
74+
// Returns true if there are no ongoing calls that already take more than
75+
// "max_interval_ms_" and that no completed call took more than
76+
// "max_interval_ms_" after "timestamp" time.
77+
bool IsHealthy(int64 timestamp) const;
78+
79+
// Returns the current time in milliseconds.
80+
static int64 MonotonicClockMillis();
81+
82+
private:
83+
// Maximum allowed duration of the healthy callback.
84+
const int max_call_duration_ms_;
85+
86+
// Function to get the current time. Defined explicitly for unit tests.
87+
std::function<int64()> fn_gettime_;
88+
89+
// Protects access to "ongoing_calls_" and "last_unhealthy_timestamp_".
90+
mutable std::mutex mu_;
91+
92+
// Linked list of currently active calls to monitor.
93+
std::list<OngoingCall> ongoing_calls_;
94+
95+
// Timestamp of the completion of last callback that lasted more than
96+
// "max_interval_ms_".
97+
int64 last_unhealthy_time_ms_;
98+
99+
DISALLOW_COPY_AND_ASSIGN(CallbacksMonitor);
100+
};
101+
102+
103+
// Automatically calls "RegisterCall", "CompleteCall" on entry and scope exit.
104+
class ScopedMonitoredCall {
105+
public:
106+
explicit ScopedMonitoredCall(const char* tag)
107+
: id_(CallbacksMonitor::GetInstance()->RegisterCall(tag)) {
108+
}
109+
110+
~ScopedMonitoredCall() {
111+
CallbacksMonitor::GetInstance()->CompleteCall(id_);
112+
}
113+
114+
private:
115+
const CallbacksMonitor::Id id_;
116+
117+
DISALLOW_COPY_AND_ASSIGN(ScopedMonitoredCall);
118+
};
119+
120+
} // namespace cdbg
121+
} // namespace devtools
122+
123+
#endif // DEVTOOLS_CDBG_COMMON_CALLBACKS_MONITOR_H_

src/agent/jvm_breakpoints_manager.cc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "jvm_breakpoints_manager.h"
1818

1919
#include <algorithm>
20+
#include "callbacks_monitor.h"
2021
#include "format_queue.h"
2122
#include "breakpoint.h"
2223
#include "jvm_evaluators.h"
@@ -86,6 +87,9 @@ void JvmBreakpointsManager::SetActiveBreakpointsList(
8687

8788
// Identify deleted and new breakpoints.
8889
{
90+
ScopedMonitoredCall monitored_call(
91+
"BreakpointsManager:SetActiveBreakpoints:Scan");
92+
8993
MutexLock lock_data(&mu_data_);
9094

9195
for (int i = 0; i < breakpoints.size(); ++i) {
@@ -119,6 +123,9 @@ void JvmBreakpointsManager::SetActiveBreakpointsList(
119123

120124
// Create new breakpoints.
121125
for (std::unique_ptr<BreakpointModel>& new_breakpoint : new_breakpoints) {
126+
ScopedMonitoredCall monitored_call(
127+
"BreakpointsManager:SetActiveBreakpoints:SetNewBreakpoint");
128+
122129
LOG(INFO) << "Setting new breakpoint: " << new_breakpoint->id;
123130

124131
std::shared_ptr<Breakpoint> jvm_breakpoint =
@@ -137,6 +144,9 @@ void JvmBreakpointsManager::SetActiveBreakpointsList(
137144

138145
// Remove breakpoints that were not listed.
139146
for (auto& breakpoint : updated_active_breakpoints) {
147+
ScopedMonitoredCall monitored_call(
148+
"BreakpointsManager:SetActiveBreakpoints:RemoveCompletedBreakpoint");
149+
140150
if (breakpoint.second != nullptr) {
141151
LOG(INFO) << "Completing breakpoint " << breakpoint.second->id()
142152
<< " (removed from active list by backend)";

src/agent/jvm_env.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@
2020
namespace devtools {
2121
namespace cdbg {
2222

23+
// Maximum time we allow the Cloud Debugger to spend inside a callback. Beyond
24+
// that we declare the agent as unhealthy. This is used for breakpoints canary.
25+
// The interval of 5 seconds is way longer than anything that the debugger
26+
// will ever take, but we need to account for potential GC cycles that
27+
// may interrupt the debugger operation.
28+
constexpr int MaxCallbackTimeMs = 5000;
29+
2330
// Gets the global instance of JVMTI interface.
2431
jvmtiEnv* jvmti();
2532

src/agent/jvmti_agent.cc

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <atomic>
2020
#include <sstream>
2121

22+
#include "callbacks_monitor.h"
2223
#include "auto_reset_event.h"
2324
#include "bridge.h"
2425
#include "config_builder.h"
@@ -120,6 +121,9 @@ JvmtiAgent::JvmtiAgent(
120121

121122

122123
JvmtiAgent::~JvmtiAgent() {
124+
// Assert no unhealthy callbacks occurred throughout the debuglet lifetime.
125+
LOG_IF(WARNING, !CallbacksMonitor::GetInstance()->IsHealthy(0))
126+
<< "Unhealthy callbacks occurred during debuglet lifetime";
123127
}
124128

125129

@@ -167,6 +171,8 @@ bool JvmtiAgent::OnLoad() {
167171

168172

169173
void JvmtiAgent::JvmtiOnVMInit(jthread thread) {
174+
ScopedMonitoredCall monitored_call("JVMTI:VMInit");
175+
170176
Stopwatch stopwatch;
171177

172178
LOG(INFO) << "Java VM started";
@@ -210,10 +216,13 @@ void JvmtiAgent::JvmtiOnVMDeath() {
210216
// to it. The class provided in JvmtiOnClassLoad doesn't have methods
211217
// initialized so it not very useful for debugger.
212218
void JvmtiAgent::JvmtiOnClassLoad(jthread thread, jclass cls) {
219+
// ScopedMonitoredCall monitored_call("JVMTI:ClassLoad");
213220
}
214221

215222

216223
void JvmtiAgent::JvmtiOnClassPrepare(jthread thread, jclass cls) {
224+
ScopedMonitoredCall monitored_call("JVMTI:ClassPrepare");
225+
217226
std::shared_ptr<Debugger> debugger = debugger_;
218227

219228
if (debugger != nullptr) {
@@ -229,12 +238,15 @@ void JvmtiAgent::JvmtiOnCompiledMethodLoad(
229238
jint map_length,
230239
const jvmtiAddrLocationMap* map,
231240
const void* compile_info) {
241+
// ScopedMonitoredCall monitored_call("JVMTI:CompiledMethodLoad");
232242
}
233243

234244

235245
void JvmtiAgent::JvmtiOnCompiledMethodUnload(
236246
jmethodID method,
237247
const void* code_addr) {
248+
ScopedMonitoredCall monitored_call("JVMTI:CompiledMethodUnload");
249+
238250
std::shared_ptr<Debugger> debugger = debugger_;
239251

240252
if (debugger != nullptr) {
@@ -247,6 +259,8 @@ void JvmtiAgent::JvmtiOnBreakpoint(
247259
jthread thread,
248260
jmethodID method,
249261
jlocation location) {
262+
ScopedMonitoredCall monitored_call("JVMTI:Breakpoint");
263+
250264
// Ignore breakpoint events from debugger worker threads. Debugging
251265
// the debugger may cause deadlock.
252266
if (JvmtiAgentThread::IsInAgentThread()) {
@@ -340,6 +354,8 @@ bool JvmtiAgent::OnWorkerReady() {
340354

341355

342356
void JvmtiAgent::OnIdle() {
357+
ScopedMonitoredCall monitored_call("Agent:Idle");
358+
343359
// Invoke scheduled callbacks.
344360
// The precision of "scheduler_" has the granularity of "OnIdle" calls. This
345361
// is typically in the order of minutes. This precision is good enough
@@ -361,6 +377,9 @@ void JvmtiAgent::OnBreakpointsUpdated(
361377

362378

363379
void JvmtiAgent::EnableDebugger(bool is_enabled) {
380+
ScopedMonitoredCall monitored_call(
381+
is_enabled ? "Agent:EnableDebugger" : "Agent:DisableDebugger");
382+
364383
if (is_enabled) {
365384
// Attach debugger if needed
366385
if (debugger_ == nullptr) {

src/agent/jvmti_globals.cc

Lines changed: 4 additions & 0 deletions

0 commit comments

Comments
 (0)