1 /*
2 * Copyright (c) 2021-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "dfx_thread.h"
17
18 #include <cerrno>
19 #include <chrono>
20 #include <climits>
21 #include <cstring>
22 #include <securec.h>
23 #include <sstream>
24 #include <sys/ptrace.h>
25 #include <sys/wait.h>
26 #include <unistd.h>
27 #include "dfx_config.h"
28 #include "dfx_define.h"
29 #include "dfx_frame_format.h"
30 #include "dfx_log.h"
31 #include "dfx_util.h"
32 #include "procinfo.h"
33
34 namespace OHOS {
35 namespace HiviewDFX {
Create(pid_t pid,pid_t tid,pid_t nsTid)36 std::shared_ptr<DfxThread> DfxThread::Create(pid_t pid, pid_t tid, pid_t nsTid)
37 {
38 auto thread = std::make_shared<DfxThread>(pid, tid, nsTid);
39 return thread;
40 }
41
DfxThread(pid_t pid,pid_t tid,pid_t nsTid)42 DfxThread::DfxThread(pid_t pid, pid_t tid, pid_t nsTid) : regs_(nullptr)
43 {
44 InitThreadInfo(pid, tid, nsTid);
45 }
46
InitThreadInfo(pid_t pid,pid_t tid,pid_t nsTid)47 void DfxThread::InitThreadInfo(pid_t pid, pid_t tid, pid_t nsTid)
48 {
49 threadInfo_.pid = pid;
50 threadInfo_.tid = tid;
51 threadInfo_.nsTid = nsTid;
52 ReadThreadName(threadInfo_.tid, threadInfo_.threadName);
53 threadStatus = ThreadStatus::THREAD_STATUS_INIT;
54 }
55
~DfxThread()56 DfxThread::~DfxThread()
57 {
58 threadStatus = ThreadStatus::THREAD_STATUS_INVALID;
59 }
60
GetThreadRegs() const61 std::shared_ptr<DfxRegs> DfxThread::GetThreadRegs() const
62 {
63 return regs_;
64 }
65
SetThreadRegs(const std::shared_ptr<DfxRegs> & regs)66 void DfxThread::SetThreadRegs(const std::shared_ptr<DfxRegs> ®s)
67 {
68 regs_ = regs;
69 }
70
AddFrame(std::shared_ptr<DfxFrame> frame)71 void DfxThread::AddFrame(std::shared_ptr<DfxFrame> frame)
72 {
73 frames_.push_back(frame);
74 }
75
GetFrames() const76 const std::vector<std::shared_ptr<DfxFrame>>& DfxThread::GetFrames() const
77 {
78 return frames_;
79 }
80
ToString() const81 std::string DfxThread::ToString() const
82 {
83 if (frames_.size() == 0) {
84 return "No frame info";
85 }
86
87 std::stringstream ss;
88 ss << "Thread name:" << threadInfo_.threadName << "" << std::endl;
89 for (size_t i = 0; i < frames_.size(); i++) {
90 if (frames_[i] == nullptr) {
91 continue;
92 }
93 ss << DfxFrameFormat::GetFrameStr(frames_[i]);
94 }
95 return ss.str();
96 }
97
Detach()98 void DfxThread::Detach()
99 {
100 if (threadStatus == ThreadStatus::THREAD_STATUS_ATTACHED) {
101 ptrace(PTRACE_CONT, threadInfo_.nsTid, 0, 0);
102 ptrace(PTRACE_DETACH, threadInfo_.nsTid, NULL, NULL);
103 threadStatus = ThreadStatus::THREAD_STATUS_INIT;
104 }
105 }
106
Attach()107 bool DfxThread::Attach()
108 {
109 if (threadStatus == ThreadStatus::THREAD_STATUS_ATTACHED) {
110 return true;
111 }
112
113 if (ptrace(PTRACE_SEIZE, threadInfo_.nsTid, 0, 0) != 0) {
114 DFXLOG_WARN("Failed to seize thread(%d:%d) from (%d:%d), errno=%d",
115 threadInfo_.tid, threadInfo_.nsTid, getuid(), getgid(), errno);
116 return false;
117 }
118
119 if (ptrace(PTRACE_INTERRUPT, threadInfo_.nsTid, 0, 0) != 0) {
120 DFXLOG_WARN("Failed to interrupt thread(%d:%d) from (%d:%d), errno=%d",
121 threadInfo_.tid, threadInfo_.nsTid, getuid(), getgid(), errno);
122 ptrace(PTRACE_DETACH, threadInfo_.nsTid, NULL, NULL);
123 return false;
124 }
125
126 int64_t startTime = std::chrono::duration_cast<std::chrono::milliseconds>(
127 std::chrono::system_clock::now().time_since_epoch()).count();
128 do {
129 if (waitpid(threadInfo_.nsTid, nullptr, WNOHANG) > 0) {
130 break;
131 }
132 int64_t curTime = std::chrono::duration_cast<std::chrono::milliseconds>(
133 std::chrono::system_clock::now().time_since_epoch()).count();
134 if (curTime - startTime > 1000) { // 1000 : 1s timeout
135 ptrace(PTRACE_DETACH, threadInfo_.nsTid, NULL, NULL);
136 DFXLOG_WARN("Failed to wait thread(%d:%d) attached.", threadInfo_.tid, threadInfo_.nsTid);
137 return false;
138 }
139 usleep(5); // 5 : sleep 5us
140 } while (true);
141 threadStatus = ThreadStatus::THREAD_STATUS_ATTACHED;
142 return true;
143 }
144 } // namespace HiviewDFX
145 } // nampespace OHOS
146