blog

ソースコードからANRログを生成する方法 (v)

上記は、ソケットサーバーから返されたレポを...

Mar 28, 2020 · 8 min. read
シェア

5.1

3.4のコードがピックアップされた後、解析は下の行に進み、レスポンスを受信した後、レスポンスからRegisteredかどうかを判断し、send_signal()メソッドを呼び出してSIGQUITシグナルを送信し、anrが発生したpidに対応するログのダンプを開始します。

次に、[art]/[runtime]/[runtime.cc]で、SIGQUITシグナルがどのように処理されるか見てみましょう。
bool debuggerd_trigger_dump(pid_t tid, DebuggerdDumpType dump_type, unsigned int timeout_ms,
 unique_fd output_fd) {
 // Check to make sure we've successfully registered.
 InterceptResponse response;
 // .4レスポンスで、対応するkRegistered判定を行う。
 rc = TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
 if (response.status != InterceptStatus::kRegistered) {
 LOG(ERROR) << "libdebuggerd_client: unexpected registration response: "
 << static_cast(response.status);
 return false;
 }
 //でSIGQUITシグナルを送信する。/Q_Mainline/art/runtime/runtime.ccシグナルは
 if (!send_signal(tid, dump_type)) {
 return false;
 } 
}

5.2art/runtime/runtime.cc

初期化メソッドInit()では、init時にSIGQUITが登録され、Signals.Add(SIGQUIT)を通してBlockSignals()で登録されます。

そしてコメントから、Runtime::Init() -> LoadNativeBridge() -> Runtime::Start()のロジックを見つけ、Runtime::Start()を見てみます。
bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
 //BlockSignals() で、SIGQUIT シグナルを登録する。
 BlockSignals();
 // Look for a native bridge.
 //
 // The intended flow here is, in the case of a running system:
 //
 // Runtime::Init() (zygote):
 // LoadNativeBridge -> dlopen from cmd line parameter.
 // |
 // V
 // Runtime::Start() (zygote):
 // No-op wrt native bridge.
 // |
 // | start app
 // V
 // DidForkFromZygote(action)
 // action = kUnload -> dlclose native bridge.
 // action = kInitialize -> initialize library
 //
 //
 // The intended flow here is, in the case of a simple dalvikvm call:
 //
 // Runtime::Init():
 // LoadNativeBridge -> dlopen from cmd line parameter.
 // |
 // V
 // Runtime::Start():
 // DidForkFromZygote(kInitialize) -> try to initialize any native bridge given.
 // No-op wrt native bridge.
 {
 //コメントから、LoadNativeBridge()は結局、Runtimeを呼び出している。::Start()
 std::string native_bridge_file_name = runtime_options.ReleaseOrDefault(Opt::NativeBridge);
 is_native_bridge_loaded_ = LoadNativeBridge(native_bridge_file_name);
 } 
}
void Runtime::BlockSignals() {
 SignalSet signals;
 signals.Add(SIGPIPE);
 // SIGQUIT is used to dump the runtime's state (including stack traces).
 signals.Add(SIGQUIT);
 // SIGUSR1 is used to initiate a GC.
 signals.Add(SIGUSR1);
 signals.Block();
}

5.3

InitNonZygoteOrPostFork()を呼び出したRuntime::Start()

bool Runtime::Start() {
 if (!is_zygote_) {
 InitNonZygoteOrPostFork(self->GetJniEnv(), /* is_system_server= */ false, action, GetInstructionSetString(kRuntimeISA));
 }

5.4

Runtime::InitNonZygoteOrPostFork()、StartSignalCatcher()と呼ばれます。

void Runtime::InitNonZygoteOrPostFork(JNIEnv* env, bool is_system_server, NativeBridgeAction action, const char* isa, bool profile_system_server) {
 StartSignalCatcher();
}

5.5

void Runtime::StartSignalCatcher() {
 if (!is_zygote_) {
 signal_catcher_ = new SignalCatcher();
 }
}

5.6art/runtime/signal_catcher.cc

SignalCatcher::SignalCatcher()は、コンストラクタ・メソッドを通じてSignalCatcher:オブジェクトを生成し、コンストラクタ・メソッドでは、コメントから分かるように、Run()が実行されます。

SignalCatcher::SignalCatcher(): lock_("SignalCatcher lock"),cond_("SignalCatcher::cond_", lock_),thread_(nullptr) {
 SetHaltFlag(false);
 // Create a raw pthread; its start routine will attach to the runtime.
 CHECK_PTHREAD_CALL(pthread_create, (&pthread_, nullptr, &Run, this), "signal catcher thread");
}

5.7

void* SignalCatcher::Run(void* arg) {
 while (true) {
 int signal_number = signal_catcher->WaitForSignal(self, signals);
 if (signal_catcher->ShouldHalt()) {
 runtime->DetachCurrentThread();
 return nullptr;
 }
 switch (signal_number) {
 case SIGQUIT:
 signal_catcher->HandleSigQuit();
 break;
 }
 }
}

5.8

SignalCatcher::HandleSigQuit()では、このメソッドはosstringstream型のosストリームを作成し、出力が必要なすべてのログをそこに格納します。

1, DumpForSigQuit()を呼び出して異なるログを収集します。
2, Output(os.str())を使って、これらのログをファイルに出力します。
void SignalCatcher::HandleSigQuit() {
 Runtime* runtime = Runtime::Current();
 std::ostringstream os;
 os << "
"
 << "----- pid " << getpid() << " at " << GetIsoDate() << " -----
";
 DumpCmdLine(os);
 // Note: The strings "Build fingerprint:" and "ABI:" are chosen to match the format used by
 // debuggerd. This allows, for example, the stack tool to work.
 std::string fingerprint = runtime->GetFingerprint();
 os << "Build fingerprint: '" << (fingerprint.empty() ? "unknown" : fingerprint) << "'
";
 os << "ABI: '" << GetInstructionSetString(runtime->GetInstructionSet()) << "'
";
 os << "Build type: " << (kIsDebugBuild ? "debug" : "optimized") << "
";
 runtime->DumpForSigQuit(os);
 os << "----- end " << getpid() << " -----
";
 Output(os.str());
}

5.9

Runtime::DumpForSigQuit()は、c++のポリモーフィックな性質によって、対応するDumpForSigQuit()を通じて対応するログを収集します。

void Runtime::DumpForSigQuit(std::ostream& os) {
 GetClassLinker()->DumpForSigQuit(os);
 GetInternTable()->DumpForSigQuit(os);
 //jvmlogを収集する
 GetJavaVM()->DumpForSigQuit(os);
 //総割り当て数などのメモリ使用ログを収集する。
 GetHeap()->DumpForSigQuit(os);
 oat_file_manager_->DumpForSigQuit(os);
 if (GetJit() != nullptr) {
 GetJit()->DumpForSigQuit(os);
 } else {
 os << "Running non JIT
";
 }
 DumpDeoptimizations(os);
 TrackedAllocators::Dump(os);
 os << "
";
 thread_list_->DumpForSigQuit(os);
 BaseMutex::DumpAll(os);
}

5.10

void SignalCatcher::Output(const std::string& s) {
 ScopedThreadStateChange tsc(Thread::Current(), kWaitingForSignalCatcherOutput);
 PaletteStatus status = PaletteWriteCrashThreadStacks(s.data(), s.size());
 if (status == PaletteStatus::kOkay) {
 LOG(INFO) << "Wrote stack traces to tombstoned";
 } else {
 CHECK(status == PaletteStatus::kFailedCheckLog);
 LOG(ERROR) << "Failed to write stack traces to tombstoned";
 }
}

5.11

PaletteStatus PaletteWriteCrashThreadStacks()は、ここで2つのユニーク_fd(スマートファイル記述子、および自動回復に似たスマートポインタとみなすことができる)を作成し、tombstoned_connect()を介してソケットサーバーに接続します。データ通信は、output_fdを取得し、ログ内のosストリームにWriteFully()メソッドを介して、output_fdに書き込み、最初にtombstoned_connectこのメソッドを分析し、それがより重要です!

パラメータ: stacks = os.data(), stacks_len = os.size()

enum PaletteStatus PaletteWriteCrashThreadStacks(/*in*/const char* stacks, size_t stacks_len) {
 android::base::unique_fd tombstone_fd;
 android::base::unique_fd output_fd;
 //output_fd = pipe_write.get()
 if (!tombstoned_connect(getpid(), &tombstone_fd, &output_fd, kDebuggerdJavaBacktrace)) {
 // Failure here could be due to file descriptor resource exhaustion
 // so write the stack trace message to the log in case it helps
 // debug that.
 LOG(INFO) << std::string_view(stacks, stacks_len);
 // tombstoned_connect() logs failure reason.
 return PaletteStatus::kFailedCheckLog;
 }
 PaletteStatus status = PaletteStatus::kOkay;
 //スタックを出力に書き出す_fd = pipe_write.get()そして/Q_Mainline/system/core/debuggerd/client/debuggerd_client.cppデバッガーd_trigger_dump()パイプによるメソッドでは_read.get()読み出す
 if (!android::base::WriteFully(output_fd, stacks, stacks_len)) {
 PLOG(ERROR) << "Failed to write tombstoned output";
 TEMP_FAILURE_RETRY(ftruncate(output_fd, 0));
 status = PaletteStatus::kFailedCheckLog;
 }
}

5.12

tombstoned_connect()、このメソッドはsystem/core/debuggerd/tombstoned/tombstoned_client.cppに実装されています。

この方法では
2、書き込み()関数を呼び出すには、ソケットサーバーにデータを書き込み、サービス側で受信
3, ファイルディスクリプタ output_fd = pipe_write.get() がソケットサーバから取得され、8.4の WriteFully() メソッドで書き込みに使用されます。
次に、ソケットサーバーが何をするのかを見てみましょう。
パラメータ: pid = getpid(), tombstoned_socket = tombstone_fd = null, output_fd = output_fd = null, dump_type = kDebuggerdJavaBacktrace
bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* output_fd,
 DebuggerdDumpType dump_type) {
 unique_fd sockfd(
 socket_local_client((dump_type != kDebuggerdJavaBacktrace ? kTombstonedCrashSocketName
 : kTombstonedJavaTraceSocketName),
 ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
 TombstonedCrashPacket packet = {};
 packet.packet_type = CrashPacketType::kDumpRequest;
 packet.packet.dump_request.pid = pid;
 packet.packet.dump_request.dump_type = dump_type;
 if (TEMP_FAILURE_RETRY(write(sockfd, &packet, sizeof(packet))) != sizeof(packet)) {
 async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to write DumpRequest packet: %s",
 strerror(errno));
 return false;
 }
 unique_fd tmp_output_fd;
 // .4SendFileDescriptorsを送信することで
 //packet = response = {.packet_type = CrashPacketType::kPerformDump}
 //tmp_output_fd = output_fd = pipe_write.get()
 ssize_t rc = ReceiveFileDescriptors(sockfd, &packet, sizeof(packet), &tmp_output_fd);
 *tombstoned_socket = std::move(sockfd);
 //output_fd = tmp_output_fd = output_fd = pipe_write.get()
 *output_fd = std::move(tmp_output_fd);
 return true;
}
Read next

SpringBootプロジェクトの複数環境設定

一般に、実際のプロジェクトでは、dev、test、prodに大別されます。それぞれの環境では、インターフェースのアドレスやデータベース接続の設定など、いくつかのパラメータが異なります。設定ファイルの頻繁な修正を避けるために、様々な環境設定を簡単に切り替えたい場合、複数の環境設定項目が必要で、プログラマブルな環境設定を提供します。

Mar 28, 2020 · 3 min read