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