1 /*
2 Copyright (c) 2011, 2021, Oracle and/or its affiliates.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is also distributed with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have included with MySQL.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License, version 2.0, for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 #include "thrman.hpp"
26 #include <mt.hpp>
27 #include <signaldata/DbinfoScan.hpp>
28
29 #include <EventLogger.hpp>
30
31 #define JAM_FILE_ID 440
32
33 extern EventLogger * g_eventLogger;
34
Thrman(Block_context & ctx,Uint32 instanceno)35 Thrman::Thrman(Block_context & ctx, Uint32 instanceno) :
36 SimulatedBlock(THRMAN, ctx, instanceno)
37 {
38 BLOCK_CONSTRUCTOR(Thrman);
39
40 addRecSignal(GSN_DBINFO_SCANREQ, &Thrman::execDBINFO_SCANREQ);
41 addRecSignal(GSN_CONTINUEB, &Thrman::execCONTINUEB);
42 addRecSignal(GSN_GET_CPU_USAGE_REQ, &Thrman::execGET_CPU_USAGE_REQ);
43 addRecSignal(GSN_READ_CONFIG_REQ, &Thrman::execREAD_CONFIG_REQ);
44 addRecSignal(GSN_STTOR, &Thrman::execSTTOR);
45
46 current_cpu_load = 90;
47 }
48
~Thrman()49 Thrman::~Thrman()
50 {
51 }
52
BLOCK_FUNCTIONS(Thrman)53 BLOCK_FUNCTIONS(Thrman)
54
55 void Thrman::execREAD_CONFIG_REQ(Signal *signal)
56 {
57 jamEntry();
58
59 /* Receive signal */
60 const ReadConfigReq * req = (ReadConfigReq*)signal->getDataPtr();
61 Uint32 ref = req->senderRef;
62 Uint32 senderData = req->senderData;
63
64 /* Send return signal */
65 ReadConfigConf * conf = (ReadConfigConf*)signal->getDataPtrSend();
66 conf->senderRef = reference();
67 conf->senderData = senderData;
68 sendSignal(ref, GSN_READ_CONFIG_CONF, signal,
69 ReadConfigConf::SignalLength, JBB);
70 }
71
72 void
execSTTOR(Signal * signal)73 Thrman::execSTTOR(Signal *signal)
74 {
75 jamEntry();
76
77 const Uint32 startPhase = signal->theData[1];
78
79 switch (startPhase) {
80 case 1:
81 Ndb_GetRUsage(&last_rusage);
82 prev_cpu_usage_check = NdbTick_getCurrentTicks();
83 sendNextCONTINUEB(signal);
84 break;
85 default:
86 ndbrequire(false);
87 }
88 sendSTTORRY(signal);
89 }
90
91 void
sendSTTORRY(Signal * signal)92 Thrman::sendSTTORRY(Signal* signal)
93 {
94 signal->theData[0] = 0;
95 signal->theData[3] = 1;
96 signal->theData[4] = 255; // No more start phases from missra
97 BlockReference cntrRef = !isNdbMtLqh() ? NDBCNTR_REF : THRMAN_REF;
98 sendSignal(cntrRef, GSN_STTORRY, signal, 6, JBB);
99 }
100
101 void
execCONTINUEB(Signal * signal)102 Thrman::execCONTINUEB(Signal *signal)
103 {
104 ndbrequire(signal->theData[0] == ZCONTINUEB_MEASURE_CPU_USAGE);
105 measure_cpu_usage();
106 sendNextCONTINUEB(signal);
107 }
108
109 void
sendNextCONTINUEB(Signal * signal)110 Thrman::sendNextCONTINUEB(Signal *signal)
111 {
112 signal->theData[0] = ZCONTINUEB_MEASURE_CPU_USAGE;
113 sendSignalWithDelay(reference(),
114 GSN_CONTINUEB,
115 signal,
116 1000,
117 1);
118 }
119
120 void
measure_cpu_usage(void)121 Thrman::measure_cpu_usage(void)
122 {
123 struct ndb_rusage curr_rusage;
124 Uint64 elapsed_micros;
125
126 Uint64 user_micros;
127 Uint64 kernel_micros;
128 Uint64 total_micros;
129
130 /**
131 * Start by making a new CPU usage measurement. After that we will
132 * measure how much time has passed since last measurement and from
133 * this we can calculate a percentage of CPU usage that this thread
134 * has had for the last second or so.
135 */
136 int res = Ndb_GetRUsage(&curr_rusage);
137 if (res != 0)
138 {
139 #ifdef DEBUG_CPU_USAGE
140 ndbout << "res = " << -res << endl;
141 #endif
142 }
143 NDB_TICKS curr_time = NdbTick_getCurrentTicks();
144 if ((curr_rusage.ru_utime == 0 && curr_rusage.ru_stime == 0) ||
145 (last_rusage.ru_utime == 0 && last_rusage.ru_stime == 0))
146 {
147 /**
148 * We lack at least one valid measurement to make any conclusions.
149 * We set CPU usage to 90 as the default value.
150 */
151 current_cpu_load = default_cpu_load;
152 }
153 else
154 {
155
156 elapsed_micros = NdbTick_Elapsed(prev_cpu_usage_check,
157 curr_time).microSec();
158
159 user_micros = curr_rusage.ru_utime - last_rusage.ru_utime;
160 kernel_micros = curr_rusage.ru_stime - last_rusage.ru_stime;
161 total_micros = user_micros + kernel_micros;
162
163 if (elapsed_micros != 0)
164 {
165 /* Handle errors in time measurements */
166 current_cpu_load = (total_micros * 100) / elapsed_micros;
167 }
168 if (current_cpu_load > 100)
169 {
170 /* Handle errors in time measurements */
171 current_cpu_load = 100;
172 }
173 }
174 #ifdef DEBUG_CPU_USAGE
175 if (current_cpu_load != 0)
176 {
177 ndbout << "CPU usage is now " << current_cpu_load << "%";
178 ndbout << " in instance " << instance() << endl;
179 }
180 #endif
181 last_rusage = curr_rusage;
182 prev_cpu_usage_check = curr_time;
183 }
184
185 void
execGET_CPU_USAGE_REQ(Signal * signal)186 Thrman::execGET_CPU_USAGE_REQ(Signal *signal)
187 {
188 signal->theData[0] = current_cpu_load;
189 }
190
191 void
execDBINFO_SCANREQ(Signal * signal)192 Thrman::execDBINFO_SCANREQ(Signal* signal)
193 {
194 jamEntry();
195
196 DbinfoScanReq req= *(DbinfoScanReq*)signal->theData;
197 const Ndbinfo::ScanCursor* cursor =
198 CAST_CONSTPTR(Ndbinfo::ScanCursor, DbinfoScan::getCursorPtr(&req));
199 Ndbinfo::Ratelimit rl;
200
201 switch(req.tableId) {
202 case Ndbinfo::THREADBLOCKS_TABLEID: {
203 Uint32 arr[NO_OF_BLOCKS];
204 Uint32 len = mt_get_blocklist(this, arr, NDB_ARRAY_SIZE(arr));
205 Uint32 pos = cursor->data[0];
206 for (; ; )
207 {
208 Ndbinfo::Row row(signal, req);
209 row.write_uint32(getOwnNodeId());
210 row.write_uint32(getThreadId()); // thr_no
211 row.write_uint32(blockToMain(arr[pos])); // block_number
212 row.write_uint32(blockToInstance(arr[pos])); // block_instance
213 ndbinfo_send_row(signal, req, row, rl);
214
215 pos++;
216 if (pos == len)
217 {
218 jam();
219 break;
220 }
221 else if (rl.need_break(req))
222 {
223 jam();
224 ndbinfo_send_scan_break(signal, req, rl, pos);
225 return;
226 }
227 }
228 break;
229 }
230 case Ndbinfo::THREADSTAT_TABLEID:{
231 ndb_thr_stat stat;
232 mt_get_thr_stat(this, &stat);
233 Ndbinfo::Row row(signal, req);
234 row.write_uint32(getOwnNodeId());
235 row.write_uint32(getThreadId()); // thr_no
236 row.write_string(stat.name);
237 row.write_uint64(stat.loop_cnt);
238 row.write_uint64(stat.exec_cnt);
239 row.write_uint64(stat.wait_cnt);
240 row.write_uint64(stat.local_sent_prioa);
241 row.write_uint64(stat.local_sent_priob);
242 row.write_uint64(stat.remote_sent_prioa);
243 row.write_uint64(stat.remote_sent_priob);
244
245 row.write_uint64(stat.os_tid);
246 row.write_uint64(NdbTick_CurrentMillisecond());
247
248 struct ndb_rusage os_rusage;
249 Ndb_GetRUsage(&os_rusage);
250 row.write_uint64(os_rusage.ru_utime);
251 row.write_uint64(os_rusage.ru_stime);
252 row.write_uint64(os_rusage.ru_minflt);
253 row.write_uint64(os_rusage.ru_majflt);
254 row.write_uint64(os_rusage.ru_nvcsw);
255 row.write_uint64(os_rusage.ru_nivcsw);
256 ndbinfo_send_row(signal, req, row, rl);
257 break;
258 }
259 default:
260 break;
261 }
262
263 ndbinfo_send_scan_conf(signal, req, rl);
264 }
265
ThrmanProxy(Block_context & ctx)266 ThrmanProxy::ThrmanProxy(Block_context & ctx) :
267 LocalProxy(THRMAN, ctx)
268 {
269 }
270
~ThrmanProxy()271 ThrmanProxy::~ThrmanProxy()
272 {
273 }
274
275 SimulatedBlock*
newWorker(Uint32 instanceNo)276 ThrmanProxy::newWorker(Uint32 instanceNo)
277 {
278 return new Thrman(m_ctx, instanceNo);
279 }
280
281