1 /*
2  * Copyright (C) 2022 The Android Open Source Project
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 #define LOG_TAG "ThreadSnapshot"
18 #include <utils/Log.h>
19 #include <utils/Timers.h>
20 #include <mediautils/ThreadSnapshot.h>
21 
22 #include <mediautils/Process.h>
23 
24 namespace android::mediautils {
25 
getTid() const26 pid_t ThreadSnapshot::getTid() const {
27     std::lock_guard lg(mLock);
28     return mState.mTid;
29 }
30 
setTid(pid_t tid)31 void ThreadSnapshot::setTid(pid_t tid) {
32     std::lock_guard lg(mLock);
33     if (mState.mTid == tid) return;
34     mState.reset(tid);
35 }
36 
reset()37 void ThreadSnapshot::reset() {
38     std::lock_guard lg(mLock);
39     mState.reset(mState.mTid);
40 }
41 
onBegin()42 void ThreadSnapshot::onBegin() {
43     std::string sched = getThreadSchedAsString(getTid()); // tid could race here,
44                                                           // accept as benign.
45     std::lock_guard lg(mLock);
46     mState.onBegin(std::move(sched));
47 }
48 
onEnd()49 void ThreadSnapshot::onEnd() {
50     std::lock_guard lg(mLock);
51     mState.onEnd();
52 }
53 
toString() const54 std::string ThreadSnapshot::toString() const {
55     // Make a local copy of the stats data under lock.
56     State state;
57     {
58         std::lock_guard lg(mLock);
59         state = mState;
60     }
61     return state.toString();
62 }
63 
reset(pid_t tid)64 void ThreadSnapshot::State::reset(pid_t tid) {
65     mTid = tid;
66     mBeginTimeNs = -2;
67     mEndTimeNs = -1;
68     mCumulativeTimeNs = 0;
69     mBeginSched.clear();
70 }
71 
onBegin(std::string sched)72 void ThreadSnapshot::State::onBegin(std::string sched) {
73     if (mBeginTimeNs < mEndTimeNs) {
74         mBeginTimeNs = systemTime();
75         mBeginSched = std::move(sched);
76     }
77 }
78 
onEnd()79 void ThreadSnapshot::State::onEnd() {
80     if (mEndTimeNs < mBeginTimeNs) {
81         mEndTimeNs = systemTime();
82         mCumulativeTimeNs += mEndTimeNs - mBeginTimeNs;
83     }
84 }
85 
toString() const86 std::string ThreadSnapshot::State::toString() const {
87     if (mBeginTimeNs < 0) return {};  // never begun.
88 
89     // compute time intervals.
90     const int64_t nowNs = systemTime();
91     int64_t cumulativeTimeNs = mCumulativeTimeNs;
92     int64_t diffNs = mEndTimeNs - mBeginTimeNs; // if onEnd() isn't matched, diffNs < 0.
93     if (diffNs < 0) {
94         diffNs = nowNs - mBeginTimeNs;
95         cumulativeTimeNs += diffNs;
96     }
97     // normalization for rate variables
98     const double lastRunPerSec =  1e9 / diffNs;
99     const double totalPerSec = 1e9 / cumulativeTimeNs;
100 
101     // HANDLE THE SCHEDULER STATISTICS HERE
102     // current and differential statistics for the scheduler.
103     std::string schedNow = getThreadSchedAsString(mTid);
104     const auto schedMapThen = parseThreadSchedString(mBeginSched);
105     const auto schedMapNow = parseThreadSchedString(schedNow);
106     static const char * schedDiffKeyList[] = {
107         "se.sum_exec_runtime",
108         "se.nr_migrations",
109         "se.statistics.wait_sum",
110         "se.statistics.wait_count",
111         "se.statistics.iowait_sum",
112         "se.statistics.iowait_count",
113         "se.statistics.nr_forced_migrations",
114         "nr_involuntary_switches",
115     };
116 
117     // compute differential rate statistics.
118     std::string diffString;
119     for (const auto diffKey : schedDiffKeyList) {
120         if (auto itThen = schedMapThen.find(diffKey);
121                 itThen != schedMapThen.end()) {
122 
123             if (auto itNow = schedMapNow.find(diffKey);
124                     itNow != schedMapNow.end()) {
125                 auto diff = itNow->second - itThen->second;
126                 diff *= lastRunPerSec;
127                 auto total = itNow->second * totalPerSec;
128                 diffString.append(diffKey).append("  last-run:")
129                         .append(std::to_string(diff))
130                         .append("  cumulative:")
131                         .append(std::to_string(total))
132                         .append("\n");
133             }
134         }
135     }
136 
137     if (!diffString.empty()) {
138         schedNow.append("*** per second stats ***\n").append(diffString);
139     }
140 
141     // Return snapshot string.
142     return schedNow;
143 }
144 
145 } // android::mediautils
146