| 129 | } |
| 130 | |
| 131 | bool JavaScriptANRDetector::onANR(JavaScriptTaskScheduler& taskScheduler, |
| 132 | std::chrono::steady_clock::duration detectionThreshold, |
| 133 | const std::atomic<bool>& ack) { |
| 134 | auto stacktraces = taskScheduler.captureStackTraces(std::chrono::seconds(kCaptureStacktraceTimeoutSeconds)); |
| 135 | StringBox moduleName; |
| 136 | std::string message; |
| 137 | |
| 138 | if (!stacktraces.empty() && stacktraces[0].getContext() != nullptr) { |
| 139 | moduleName = stacktraces[0].getContext()->getPath().getResourceId().bundleName; |
| 140 | } |
| 141 | |
| 142 | if (!hasRunningStacktrace(stacktraces) && ack.load()) { |
| 143 | // The ack was submitted while waiting to capture stacktraces. |
| 144 | // We consider that task scheduler has recovered |
| 145 | return true; |
| 146 | } |
| 147 | |
| 148 | auto detectionThresholdString = |
| 149 | snap::utils::time::Duration<std::chrono::steady_clock>(detectionThreshold).toString(); |
| 150 | if (moduleName.isEmpty()) { |
| 151 | message = fmt::format("Detected unattributed ANR after {}", detectionThresholdString); |
| 152 | } else { |
| 153 | message = fmt::format("Detected ANR in '{}' after {}", moduleName, detectionThresholdString); |
| 154 | } |
| 155 | |
| 156 | if (stacktraces.empty()) { |
| 157 | message += " but unable to capture stack traces."; |
| 158 | } |
| 159 | |
| 160 | // getCurrentModuleLoadInfo() reads saved native state, not JS, so it is safe to call while the |
| 161 | // ANR has the JS thread stuck. |
| 162 | auto moduleLoadInfo = taskScheduler.getCurrentModuleLoadInfo(); |
| 163 | if (!moduleLoadInfo.empty()) { |
| 164 | message += " [module-load: " + moduleLoadInfo + "]"; |
| 165 | } |
| 166 | |
| 167 | VALDI_ERROR(*_logger, "{}", message); |
| 168 | for (const auto& stacktrace : stacktraces) { |
| 169 | if (stacktrace.getStatus() == JavaScriptCapturedStacktrace::Status::RUNNING) { |
| 170 | VALDI_ERROR(*_logger, "ANR Stacktrace:\n{}", stacktrace.getStackTrace()); |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | Ref<Metrics> metrics; |
| 175 | Ref<IJavaScriptANRDetectorListener> listener; |
| 176 | { |
| 177 | std::lock_guard<Mutex> lock(_mutex); |
| 178 | metrics = _metrics; |
| 179 | listener = _listener; |
| 180 | } |
| 181 | |
| 182 | if (metrics != nullptr) { |
| 183 | if (moduleName.isEmpty()) { |
| 184 | metrics->emitANR(); |
| 185 | } else { |
| 186 | metrics->emitANR(moduleName); |
| 187 | } |
| 188 | } |
nothing calls this directly
no test coverage detected