• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

taosdata / TDengine / #4747

21 Sep 2025 11:53PM UTC coverage: 58.002% (-1.1%) from 59.065%
#4747

push

travis-ci

web-flow
fix: refine python taos error log matching in checkAsan.sh (#33029)

* fix: refine python taos error log matching in checkAsan.sh

* fix: improve python taos error log matching in checkAsan.sh

133398 of 293157 branches covered (45.5%)

Branch coverage included in aggregate %.

201778 of 284713 relevant lines covered (70.87%)

5539418.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.32
/source/os/src/osSysinfo.c
1
/*
2
 * Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
3
 *
4
 * This program is free software: you can use, redistribute, and/or modify
5
 * it under the terms of the GNU Affero General Public License, version 3
6
 * or later ("AGPL"), as published by the Free Software Foundation.
7
 *
8
 * This program is distributed in the hope that it will be useful, but WITHOUT
9
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10
 * FITNESS FOR A PARTICULAR PURPOSE.
11
 *
12
 * You should have received a copy of the GNU Affero General Public License
13
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14
 */
15

16
#define _DEFAULT_SOURCE
17
#include "os.h"
18
#include "taoserror.h"
19
#include "cus_name.h"
20

21
#define PROCESS_ITEM 12
22
#define UUIDLEN37 37
23

24
typedef struct {
25
  uint64_t user;
26
  uint64_t nice;
27
  uint64_t system;
28
  uint64_t idle;
29
  uint64_t wa;
30
  uint64_t hi;
31
  uint64_t si;
32
  uint64_t st;
33
  uint64_t guest;
34
  uint64_t guest_nice;
35
} SysCpuInfo;
36

37
typedef struct {
38
  uint64_t utime;   // user time
39
  uint64_t stime;   // kernel time
40
  uint64_t cutime;  // all user time
41
  uint64_t cstime;  // all dead time
42
} ProcCpuInfo;
43

44
#ifdef WINDOWS
45

46
/*
47
 * windows implementation
48
 */
49

50
#if (_WIN64)
51
#include <iphlpapi.h>
52
#include <mswsock.h>
53
#include <psapi.h>
54
#include <stdio.h>
55
#include <windows.h>
56
#include <ws2tcpip.h>
57
#pragma comment(lib, "Mswsock.lib ")
58
#endif
59

60
#include <objbase.h>
61

62
#pragma warning(push)
63
#pragma warning(disable : 4091)
64
#include <DbgHelp.h>
65
#pragma warning(pop)
66

67
LONG WINAPI FlCrashDump(PEXCEPTION_POINTERS ep) {
68
  typedef BOOL(WINAPI * FxMiniDumpWriteDump)(IN HANDLE hProcess, IN DWORD ProcessId, IN HANDLE hFile,
69
                                             IN MINIDUMP_TYPE                           DumpType,
70
                                             IN CONST PMINIDUMP_EXCEPTION_INFORMATION   ExceptionParam,
71
                                             IN CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
72
                                             IN CONST PMINIDUMP_CALLBACK_INFORMATION    CallbackParam);
73

74
  HMODULE dll = LoadLibrary("dbghelp.dll");
75
  if (dll == NULL) return EXCEPTION_CONTINUE_SEARCH;
76
  FxMiniDumpWriteDump mdwd = (FxMiniDumpWriteDump)(GetProcAddress(dll, "MiniDumpWriteDump"));
77
  if (mdwd == NULL) {
78
    FreeLibrary(dll);
79
    return EXCEPTION_CONTINUE_SEARCH;
80
  }
81

82
  TCHAR path[MAX_PATH];
83
  DWORD len = GetModuleFileName(NULL, path, _countof(path));
84
  path[len - 3] = 'd';
85
  path[len - 2] = 'm';
86
  path[len - 1] = 'p';
87

88
  HANDLE file = CreateFile(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
89
  if (file == INVALID_HANDLE_VALUE) {
90
    FreeLibrary(dll);
91
    return EXCEPTION_CONTINUE_SEARCH;
92
  }
93

94
  MINIDUMP_EXCEPTION_INFORMATION mei;
95
  mei.ThreadId = GetCurrentThreadId();
96
  mei.ExceptionPointers = ep;
97
  mei.ClientPointers = FALSE;
98

99
  (*mdwd)(GetCurrentProcess(), GetCurrentProcessId(), file, MiniDumpWithHandleData, &mei, NULL, NULL);
100

101
  CloseHandle(file);
102
  FreeLibrary(dll);
103

104
  return EXCEPTION_CONTINUE_SEARCH;
105
}
106
LONG WINAPI exceptionHandler(LPEXCEPTION_POINTERS exception);
107

108
#elif defined(_TD_DARWIN_64)
109

110
#include <errno.h>
111
#include <libproc.h>
112
#include <sys/sysctl.h>
113
#include <SystemConfiguration/SCDynamicStoreCopySpecific.h>
114
#include <CoreFoundation/CFString.h>
115
#include <stdio.h>
116

117
#else
118

119
#include <argp.h>
120
#ifndef TD_ASTRA
121
#include <linux/sysctl.h>
122
#include <sys/file.h>
123
#include <sys/resource.h>
124
#include <sys/statvfs.h>
125
#include <sys/syscall.h>
126
#endif
127
#include <sys/utsname.h>
128
#include <unistd.h>
129

130
static pid_t tsProcId;
131
static char  tsSysNetFile[] = "/proc/net/dev";
132
static char  tsSysCpuFile[] = "/proc/stat";
133
static char  tsCpuPeriodFile[] = "/sys/fs/cgroup/cpu/cpu.cfs_period_us";
134
static char  tsCpuQuotaFile[] = "/sys/fs/cgroup/cpu/cpu.cfs_quota_us";
135
static char  tsProcCpuFile[25] = {0};
136
static char  tsProcMemFile[25] = {0};
137
static char  tsProcIOFile[25] = {0};
138

139
static void taosGetProcIOnfos() {
92,928✔
140
  tsPageSizeKB = sysconf(_SC_PAGESIZE) / 1024;
92,928✔
141
  tsOpenMax = sysconf(_SC_OPEN_MAX);
92,928✔
142
  tsStreamMax = TMAX(sysconf(_SC_STREAM_MAX), 0);
92,928✔
143
#ifndef TD_ASTRA
144
  tsProcId = (pid_t)syscall(SYS_gettid);
92,928✔
145

146
  (void)snprintf(tsProcMemFile, sizeof(tsProcMemFile), "/proc/%d/status", tsProcId);
92,928✔
147
  (void)snprintf(tsProcCpuFile, sizeof(tsProcCpuFile), "/proc/%d/stat", tsProcId);
92,928✔
148
  (void)snprintf(tsProcIOFile, sizeof(tsProcIOFile), "/proc/%d/io", tsProcId);
92,928✔
149
#endif
150
}
92,928✔
151
#endif
152

153
static int32_t taosGetSysCpuInfo(SysCpuInfo *cpuInfo) {
92,932✔
154
  int32_t code = 0;
92,932✔
155
#ifdef WINDOWS
156
  FILETIME pre_idleTime = {0};
157
  FILETIME pre_kernelTime = {0};
158
  FILETIME pre_userTime = {0};
159
  FILETIME idleTime;
160
  FILETIME kernelTime;
161
  FILETIME userTime;
162
  bool     res = GetSystemTimes(&idleTime, &kernelTime, &userTime);
163
  if (res) {
164
    cpuInfo->idle = CompareFileTime(&pre_idleTime, &idleTime);
165
    cpuInfo->system = CompareFileTime(&pre_kernelTime, &kernelTime);
166
    cpuInfo->user = CompareFileTime(&pre_userTime, &userTime);
167
    cpuInfo->nice = 0;
168
  }
169
#elif defined(DARWIN) || defined(TD_ASTRA)
170
  cpuInfo->idle = 0;
171
  cpuInfo->system = 0;
172
  cpuInfo->user = 0;
173
  cpuInfo->nice = 0;
174
#else
175
  TdFilePtr pFile = taosOpenFile(tsSysCpuFile, TD_FILE_READ | TD_FILE_STREAM);
92,932✔
176
  if (pFile == NULL) {
92,932!
177
    return terrno;
×
178
  }
179

180
  char    line[1024];
181
  ssize_t bytes = taosGetsFile(pFile, sizeof(line), line);
92,932✔
182
  if (bytes < 0) {
92,932!
183
    TAOS_SKIP_ERROR(taosCloseFile(&pFile));
×
184
    return terrno;
×
185
  }
186

187
  char cpu[10] = {0};
92,932✔
188
  code = sscanf(line,
92,932✔
189
         "%s %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64
190
         " %" PRIu64,
191
         cpu, &cpuInfo->user, &cpuInfo->nice, &cpuInfo->system, &cpuInfo->idle, &cpuInfo->wa, &cpuInfo->hi,
192
         &cpuInfo->si, &cpuInfo->st, &cpuInfo->guest, &cpuInfo->guest_nice);
193
  if (EOF == code) {
92,932!
194
    terrno = TAOS_SYSTEM_ERROR(ERRNO);
×
195
    TAOS_SKIP_ERROR(taosCloseFile(&pFile));
×
196
    return terrno;
×
197
  }
198
  
199
  TAOS_SKIP_ERROR(taosCloseFile(&pFile));
92,932✔
200
#endif
201

202
  return 0;
92,932✔
203
}
204

205
static int32_t taosGetProcCpuInfo(ProcCpuInfo *cpuInfo) {
92,932✔
206
  int32_t code = 0;
92,932✔
207

208
#ifdef WINDOWS
209
  FILETIME pre_krnlTm = {0};
210
  FILETIME pre_usrTm = {0};
211
  FILETIME creatTm, exitTm, krnlTm, usrTm;
212

213
  if (GetThreadTimes(GetCurrentThread(), &creatTm, &exitTm, &krnlTm, &usrTm)) {
214
    cpuInfo->stime = CompareFileTime(&pre_krnlTm, &krnlTm);
215
    cpuInfo->utime = CompareFileTime(&pre_usrTm, &usrTm);
216
    cpuInfo->cutime = 0;
217
    cpuInfo->cstime = 0;
218
  }
219
#elif defined(DARWIN) || defined(TD_ASTRA)
220
  cpuInfo->stime = 0;
221
  cpuInfo->utime = 0;
222
  cpuInfo->cutime = 0;
223
  cpuInfo->cstime = 0;
224
#else
225
  TdFilePtr pFile = taosOpenFile(tsProcCpuFile, TD_FILE_READ | TD_FILE_STREAM);
92,932✔
226
  if (pFile == NULL) {
92,932✔
227
    return terrno;
3✔
228
  }
229

230
  char    line[1024] = {0};
92,929✔
231
  ssize_t bytes = taosGetsFile(pFile, sizeof(line), line);
92,929✔
232
  if (bytes < 0) {
92,929!
233
    TAOS_SKIP_ERROR(taosCloseFile(&pFile));
×
234
    return code;
×
235
  }
236

237
  for (int i = 0, blank = 0; line[i] != 0; ++i) {
6,746,915!
238
    if (line[i] == ' ') blank++;
6,746,915✔
239
    if (blank == PROCESS_ITEM) {
6,746,915✔
240
      code = sscanf(line + i + 1, "%" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64, &cpuInfo->utime, &cpuInfo->stime,
92,929✔
241
             &cpuInfo->cutime, &cpuInfo->cstime);
242
      if (EOF == code) {
92,929!
243
        terrno = TAOS_SYSTEM_ERROR(ERRNO);
×
244
        return terrno;
×
245
      }
246
             
247
      break;
92,929✔
248
    }
249
  }
250

251
  TAOS_SKIP_ERROR(taosCloseFile(&pFile));
92,929✔
252
#endif
253

254
  return 0;
92,929✔
255
}
256

257
bool taosCheckSystemIsLittleEnd() {
4,792✔
258
  union check {
259
    int16_t i;
260
    char    ch[2];
261
  } c;
262
  c.i = 1;
4,792✔
263
  return c.ch[0] == 1;
4,792✔
264
}
265

266
void taosGetSystemInfo() {
92,928✔
267
#ifdef WINDOWS
268
  TAOS_SKIP_ERROR(taosGetCpuCores(&tsNumOfCores, false));
269
  TAOS_SKIP_ERROR(taosGetTotalMemory(&tsTotalMemoryKB));
270
  TAOS_SKIP_ERROR(taosGetCpuUsage(NULL, NULL));
271
#elif defined(_TD_DARWIN_64)
272
  long physical_pages = sysconf(_SC_PHYS_PAGES);
273
  long page_size = sysconf(_SC_PAGESIZE);
274
  tsTotalMemoryKB = physical_pages * page_size / 1024;
275
  tsPageSizeKB = page_size / 1024;
276
  tsNumOfCores = sysconf(_SC_NPROCESSORS_ONLN);
277
#elif defined(TD_ASTRA)
278
  taosGetProcIOnfos();
279
  TAOS_SKIP_ERROR(taosGetCpuCores(&tsNumOfCores, false));
280
  TAOS_SKIP_ERROR(taosGetTotalMemory(&tsTotalMemoryKB));
281
  TAOS_SKIP_ERROR(taosGetCpuUsage(NULL, NULL));
282
#else
283
  taosGetProcIOnfos();
92,928✔
284
  TAOS_SKIP_ERROR(taosGetCpuCores(&tsNumOfCores, false)); 
92,928✔
285
  TAOS_SKIP_ERROR(taosGetTotalMemory(&tsTotalMemoryKB));
92,928✔
286
  TAOS_SKIP_ERROR(taosGetCpuUsage(NULL, NULL));
92,928✔
287
  TAOS_SKIP_ERROR(taosGetCpuInstructions(&tsSSE42Supported, &tsAVXSupported, &tsAVX2Supported, &tsFMASupported, &tsAVX512Supported));
92,928✔
288
#endif
289
}
92,928✔
290

291
int32_t taosGetEmail(char *email, int32_t maxLen) {
1,934✔
292
  OS_PARAM_CHECK(email);
1,934✔
293
#ifdef WINDOWS
294
  return 0;
295
#elif defined(_TD_DARWIN_64)
296
#ifdef CUS_PROMPT
297
  const char *filepath = "/usr/local/"CUS_PROMPT"/email";
298
#else
299
  const char *filepath = "/usr/local/taos/email";
300
#endif  // CUS_PROMPT
301

302
  TdFilePtr pFile = taosOpenFile(filepath, TD_FILE_READ);
303
  if (pFile == NULL) return false;
304

305
  if (taosReadFile(pFile, (void *)email, maxLen) < 0) {
306
    taosCloseFile(&pFile);
307
    return terrno;
308
  }
309

310
  taosCloseFile(&pFile);
311
  return 0;
312
#else
313
#ifdef CUS_PROMPT
314
  const char *filepath = "/usr/local/"CUS_PROMPT"/email";
1,931✔
315
#else
316
  const char *filepath = "/usr/local/taos/email";
317
#endif  // CUS_PROMPT
318

319
  TdFilePtr pFile = taosOpenFile(filepath, TD_FILE_READ);
1,931✔
320
  if (pFile == NULL) return terrno;
1,931!
321

322
  if (taosReadFile(pFile, (void *)email, maxLen) < 0) {
×
323
    int32_t code = terrno;
×
324
    TAOS_SKIP_ERROR(taosCloseFile(&pFile));
×
325
    return code;
×
326
  }
327

328
  TAOS_SKIP_ERROR(taosCloseFile(&pFile));
×
329
  
330
  return 0;
×
331
#endif
332
}
333

334
#ifdef WINDOWS
335
bool getWinVersionReleaseName(char *releaseName, int32_t maxLen) {
336
  if(releaseName == NULL) return false;
337
  TCHAR          szFileName[MAX_PATH];
338
  DWORD             dwHandle;
339
  DWORD             dwLen;
340
  LPVOID            lpData;
341
  UINT              uLen;
342
  VS_FIXEDFILEINFO *pFileInfo;
343

344
  int ret = GetWindowsDirectory(szFileName, MAX_PATH);
345
  if (ret == 0) {
346
    return false;
347
  }
348
  wsprintf(szFileName, L"%s%s", szFileName, L"\\explorer.exe");
349
  dwLen = GetFileVersionInfoSize(szFileName, &dwHandle);
350
  if (dwLen == 0) {
351
    return false;
352
  }
353

354
  lpData = malloc(dwLen);
355
  if (lpData == NULL) return false;
356
  if (!GetFileVersionInfo(szFileName, dwHandle, dwLen, lpData)) {
357
    free(lpData);
358
    return false;
359
  }
360

361
  if (!VerQueryValue(lpData, L"\\", (LPVOID *)&pFileInfo, &uLen)) {
362
    free(lpData);
363
    return false;
364
  }
365

366
  snprintf(releaseName, maxLen, "Windows %d.%d", HIWORD(pFileInfo->dwProductVersionMS),
367
           LOWORD(pFileInfo->dwProductVersionMS));
368
  free(lpData);
369
  return true;
370
}
371
#endif
372

373
int32_t taosGetOsReleaseName(char *releaseName, char* sName, char* ver, int32_t maxLen) {
8✔
374
  OS_PARAM_CHECK(releaseName);
8✔
375
#ifdef WINDOWS
376
  if (!getWinVersionReleaseName(releaseName, maxLen)) {
377
    snprintf(releaseName, maxLen, "Windows");
378
  }
379
  if(sName) snprintf(sName, maxLen, "Windows");
380
  return 0;
381
#elif defined(_TD_DARWIN_64)
382
  char osversion[32];
383
  size_t osversion_len = sizeof(osversion) - 1;
384
  int osversion_name[] = { CTL_KERN, KERN_OSRELEASE };
385

386
  if(sName) snprintf(sName, maxLen, "macOS");
387
  if (sysctl(osversion_name, 2, osversion, &osversion_len, NULL, 0) == -1) {
388
    return TAOS_SYSTEM_ERROR(ERRNO);
389
  }
390

391
  uint32_t major, minor;
392
  if (sscanf(osversion, "%u.%u", &major, &minor) == EOF) {
393
      return TAOS_SYSTEM_ERROR(ERRNO);
394
  }
395
  if (major >= 20) {
396
      major -= 9; // macOS 11 and newer
397
      snprintf(releaseName, maxLen, "macOS %u.%u", major, minor);
398
  } else {
399
      major -= 4; // macOS 10.1.1 and newer
400
      snprintf(releaseName, maxLen, "macOS 10.%d.%d", major, minor);
401
  }
402

403
  return 0;
404
#elif defined(TD_ASTRA) // TD_ASTRA_TODO
405
  if(sName) snprintf(sName, maxLen, "Astra");
406
  snprintf(releaseName, maxLen, "Astra");
407
  return 0;
408
#else
409
  char    line[1024];
410
  char   *dest = NULL;
5✔
411
  size_t  size = 0;
5✔
412
  int32_t code = 0;
5✔
413
  int32_t cnt = 0;
5✔
414

415
  TdFilePtr pFile = taosOpenFile("/etc/os-release", TD_FILE_READ | TD_FILE_STREAM);
5✔
416
  if (pFile == NULL) {
5!
417
    return terrno;
×
418
  }
419

420
  while ((size = taosGetsFile(pFile, sizeof(line), line)) > 0) {
65✔
421
    line[size - 1] = '\0';
60✔
422
    if (strncmp(line, "NAME", 4) == 0) {
60✔
423
      dest = sName;
5✔
424
    } else if (strncmp(line, "PRETTY_NAME", 11) == 0) {
55✔
425
      dest = releaseName;
5✔
426
      code = 0;
5✔
427
    } else if (strncmp(line, "VERSION_ID", 10) == 0) {
50✔
428
      dest = ver;
5✔
429
    } else {
430
      continue;
45✔
431
    }
432
    if (!dest) continue;
15✔
433
    const char *p = strchr(line, '=') + 1;
5✔
434
    if (*p == '"') {
5!
435
      p++;
5✔
436
      line[size - 2] = 0;
5✔
437
    }
438
    tstrncpy(dest, p, maxLen);
5✔
439

440
    if (++cnt >= 3) break;
5!
441
  }
442

443
  TAOS_SKIP_ERROR(taosCloseFile(&pFile));
5✔
444
  return code;
5✔
445
#endif
446
}
447

448
int32_t taosGetCpuInfo(char *cpuModel, int32_t maxLen, float *numOfCores) {
11✔
449
  OS_PARAM_CHECK(cpuModel);
11✔
450
  OS_PARAM_CHECK(numOfCores);
8✔
451
#ifdef WINDOWS
452
  char  value[100];
453
  DWORD bufferSize = sizeof(value);
454
  LSTATUS ret = RegGetValue(HKEY_LOCAL_MACHINE, "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", "ProcessorNameString",
455
              RRF_RT_ANY, NULL, (PVOID)&value, &bufferSize);
456
  if (ret != ERROR_SUCCESS) {
457
    return TAOS_SYSTEM_ERROR(ret);
458
  }
459
  tstrncpy(cpuModel, value, maxLen);
460
  SYSTEM_INFO si;
461
  memset(&si, 0, sizeof(SYSTEM_INFO));
462
  GetSystemInfo(&si);
463
  *numOfCores = si.dwNumberOfProcessors;
464
  return 0;
465
#elif defined(_TD_DARWIN_64)
466
  char    buf[16];
467
  int32_t done = 0;
468
  int32_t code = -1;
469

470
  TdCmdPtr pCmd = taosOpenCmd("sysctl -n machdep.cpu.brand_string");
471
  if (pCmd == NULL) return code;
472
  if (taosGetsCmd(pCmd, maxLen, cpuModel) > 0) {
473
    code = 0;
474
    done |= 1;
475
  }
476
  int endPos = strlen(cpuModel)-1;
477
  if (cpuModel[endPos] == '\n') {
478
    cpuModel[endPos] = '\0';
479
  }
480
  taosCloseCmd(&pCmd);
481

482
  pCmd = taosOpenCmd("sysctl -n machdep.cpu.core_count");
483
  if (pCmd == NULL) return code;
484
  memset(buf, 0, sizeof(buf));
485
  if (taosGetsCmd(pCmd, sizeof(buf) - 1, buf) > 0) {
486
    code = 0;
487
    done |= 2;
488
    *numOfCores = taosStr2Float(buf, NULL);
489
  }
490
  taosCloseCmd(&pCmd);
491

492
  return code;
493
#elif defined(TD_ASTRA) // TD_ASTRA_TODO
494
  tstrncpy(cpuModel, "ft_2000_4", maxLen);
495
  TAOS_SKIP_ERROR(taosGetCpuCores(numOfCores, false));
496
  return 0;
497
#else
498
  char    line[1024] = {0};
5✔
499
  size_t  size = 0;
5✔
500
  int32_t done = 0;
5✔
501
  int32_t code = 0;
5✔
502
  float   coreCount = 0;
5✔
503

504
  TdFilePtr pFile = taosOpenFile("/proc/cpuinfo", TD_FILE_READ | TD_FILE_STREAM);
5✔
505
  if (pFile == NULL) return terrno;
5!
506

507
  while (done != 3 && (size = taosGetsFile(pFile, sizeof(line), line)) > 0) {
70!
508
    line[size - 1] = '\0';
65✔
509
    if (((done & 1) == 0) && strncmp(line, "model name", 10) == 0) {
65✔
510
      const char *v = strchr(line, ':') + 2;
5✔
511
      tstrncpy(cpuModel, v, maxLen);
5✔
512
      code = 0;
5✔
513
      done |= 1;
5✔
514
    } else if (((done & 2) == 0) && strncmp(line, "cpu cores", 9) == 0) {
60!
515
      const char *v = strchr(line, ':') + 2;
5✔
516
      *numOfCores = taosStr2Float(v, NULL);
5✔
517
      done |= 2;
5✔
518
    }
519
    if (strncmp(line, "processor", 9) == 0) coreCount += 1;
65✔
520
  }
521

522
  TAOS_SKIP_ERROR(taosCloseFile(&pFile));
5✔
523

524
  if (code != 0 && (done & 1) == 0) {
5!
525
    TdFilePtr pFile1 = taosOpenFile("/proc/device-tree/model", TD_FILE_READ | TD_FILE_STREAM);
×
526
    if (pFile1 != NULL) {
×
527
      ssize_t bytes = taosGetsFile(pFile1, maxLen, cpuModel);
×
528
      TAOS_SKIP_ERROR(taosCloseFile(&pFile));
×
529
      if (bytes > 0) {
×
530
        code = 0;
×
531
        done |= 1;
×
532
      }
533
    }
534
  }
535

536
  if (code != 0 && (done & 1) == 0) {
5!
537
    TdCmdPtr pCmd = taosOpenCmd("uname -a");
×
538
    if (pCmd == NULL) {
×
539
      return terrno;
×
540
    }
541
    if (taosGetsCmd(pCmd, maxLen, cpuModel) > 0) {
×
542
      code = 0;
×
543
      done |= 1;
×
544
    }
545
    taosCloseCmd(&pCmd);
×
546
  }
547

548
  if ((done & 2) == 0) {
5!
549
    *numOfCores = coreCount;
×
550
    done |= 2;
×
551
  }
552

553
  return code;
5✔
554
#endif
555
}
556

557
// Returns the container's CPU quota if successful, otherwise returns the physical CPU cores
558
static int32_t taosCntrGetCpuCores(float *numOfCores) {
95,429✔
559
#ifdef WINDOWS
560
  return TSDB_CODE_UNSUPPORT_OS;
561
#elif defined(_TD_DARWIN_64) || defined(TD_ASTRA)
562
  return TSDB_CODE_UNSUPPORT_OS;
563
#else
564
  TdFilePtr pFile = NULL;
95,429✔
565
  if (!(pFile = taosOpenFile(tsCpuQuotaFile, TD_FILE_READ | TD_FILE_STREAM))) {
95,429!
566
    goto _sys;
×
567
  }
568
  char qline[32] = {0};
95,429✔
569
  if (taosGetsFile(pFile, sizeof(qline), qline) <= 0) {
95,429!
570
    TAOS_SKIP_ERROR(taosCloseFile(&pFile));
×
571
    goto _sys;
×
572
  }
573
  
574
  TAOS_SKIP_ERROR(taosCloseFile(&pFile));
95,429✔
575
  float quota = taosStr2Float(qline, NULL);
95,429✔
576
  if (quota < 0) {
95,429!
577
    goto _sys;
95,429✔
578
  }
579

580
  if (!(pFile = taosOpenFile(tsCpuPeriodFile, TD_FILE_READ | TD_FILE_STREAM))) {
×
581
    goto _sys;
×
582
  }
583
  
584
  char pline[32] = {0};
×
585
  if (taosGetsFile(pFile, sizeof(pline), pline) <= 0) {
×
586
    TAOS_SKIP_ERROR(taosCloseFile(&pFile));
×
587
    goto _sys;
×
588
  }
589
  
590
  TAOS_SKIP_ERROR(taosCloseFile(&pFile));
×
591

592
  float period = taosStr2Float(pline, NULL);
×
593
  float quotaCores = quota / period;
×
594
  float sysCores = sysconf(_SC_NPROCESSORS_ONLN);
×
595
  if (quotaCores < sysCores && quotaCores > 0) {
×
596
    *numOfCores = quotaCores;
×
597
  } else {
598
    *numOfCores = sysCores;
×
599
  }
600
  if(*numOfCores <= 0) {
×
601
    return TAOS_SYSTEM_ERROR(ERRNO);
×
602
  }
603
  goto _end;
×
604
  
605
_sys:
95,429✔
606
  *numOfCores = sysconf(_SC_NPROCESSORS_ONLN);
95,429✔
607
  if(*numOfCores <= 0) {
95,429!
608
    return TAOS_SYSTEM_ERROR(ERRNO);
×
609
  }
610
  
611
_end:
95,429✔
612
  return 0;
95,429✔
613
  
614
#endif
615
}
616

617
int32_t taosGetCpuCores(float *numOfCores, bool physical) {
99,257✔
618
  OS_PARAM_CHECK(numOfCores);
99,257✔
619
#ifdef WINDOWS
620
  SYSTEM_INFO info;
621
  GetSystemInfo(&info);
622
  *numOfCores = info.dwNumberOfProcessors;
623
  return  0;
624
#elif defined(_TD_DARWIN_64)
625
  *numOfCores = sysconf(_SC_NPROCESSORS_ONLN);
626
  if(*numOfCores <= 0) {
627
    return TAOS_SYSTEM_ERROR(ERRNO);
628
  }
629
  return 0;
630
#elif defined(TD_ASTRA) // TD_ASTRA_TODO
631
  *numOfCores = 4;
632
  return 0;
633
#else
634
  if (physical) {
99,254✔
635
    *numOfCores = sysconf(_SC_NPROCESSORS_ONLN);
3,825✔
636
    if(*numOfCores <= 0) {
3,825!
637
      return TAOS_SYSTEM_ERROR(ERRNO);
×
638
    }
639
  } else {
640
    int code= taosCntrGetCpuCores(numOfCores);
95,429✔
641
    if(code != 0) {
95,429!
642
      return code;
×
643
    }
644
  }
645
  return 0;
99,254✔
646
#endif
647
}
648

649
int32_t taosGetCpuUsage(double *cpu_system, double *cpu_engine) {
92,932✔
650
  static int64_t lastSysUsed = -1;
651
  static int64_t lastSysTotal = -1;
652
  static int64_t lastProcTotal = -1;
653
  static int64_t curSysUsed = 0;
654
  static int64_t curSysTotal = 0;
655
  static int64_t curProcTotal = 0;
656

657
  if (cpu_system != NULL) *cpu_system = 0;
92,932✔
658
  if (cpu_engine != NULL) *cpu_engine = 0;
92,932✔
659

660
  SysCpuInfo  sysCpu = {0};
92,932✔
661
  ProcCpuInfo procCpu = {0};
92,932✔
662
  if (taosGetSysCpuInfo(&sysCpu) == 0 && taosGetProcCpuInfo(&procCpu) == 0) {
92,932!
663
    curSysUsed = sysCpu.user + sysCpu.nice + sysCpu.system + sysCpu.wa + sysCpu.hi + sysCpu.si + sysCpu.st +
92,929✔
664
                 sysCpu.guest + sysCpu.guest_nice;
92,929✔
665
    curSysTotal = curSysUsed + sysCpu.idle;
92,929✔
666
    curProcTotal = procCpu.utime + procCpu.stime + procCpu.cutime + procCpu.cstime;
92,929✔
667

668
    if(lastSysUsed >= 0 && lastSysTotal >=0 && lastProcTotal >=0){
92,929!
669
      if (curSysTotal - lastSysTotal > 0 && curSysUsed >= lastSysUsed && curProcTotal >= lastProcTotal) {
86,065!
670
        if (cpu_system != NULL) {
79,478✔
671
          *cpu_system = (curSysUsed - lastSysUsed) / (double)(curSysTotal - lastSysTotal) * 100;
1✔
672
        }
673
        if (cpu_engine != NULL) {
79,478✔
674
          *cpu_engine = (curProcTotal - lastProcTotal) / (double)(curSysTotal - lastSysTotal) * 100;
1✔
675
        }
676
      }
677
    }
678

679
    lastSysUsed = curSysUsed;
92,929✔
680
    lastSysTotal = curSysTotal;
92,929✔
681
    lastProcTotal = curProcTotal;
92,929✔
682
  }
683
  return 0;
92,932✔
684
}
685

686
#define __cpuid_fix(level, a, b, c, d) \
687
              __asm__("xor %%ecx, %%ecx\n" \
688
                      "cpuid\n" \
689
                      : "=a"(a), "=b"(b), "=c"(c), "=d"(d) \
690
                      : "0"(level))
691

692
// todo add for windows and mac
693
int32_t taosGetCpuInstructions(char* sse42, char* avx, char* avx2, char* fma, char* avx512) {
92,928✔
694
#ifdef WINDOWS
695
#elif defined(_TD_DARWIN_64)
696
#else
697

698
#ifdef _TD_X86_
699
  // Since the compiler is not support avx/avx2 instructions, the global variables always need to be
700
  // set to be false
701
  uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
92,928✔
702

703
  int32_t ret = __get_cpuid(1, &eax, &ebx, &ecx, &edx);
92,928✔
704
  if (ret == 0) {
92,928!
705
    return -1;  // failed to get the cpuid info
×
706
  }
707

708
  *sse42 = (char) ((ecx & bit_SSE4_2) == bit_SSE4_2);
92,928✔
709
  *avx   = (char) ((ecx & bit_AVX) == bit_AVX);
92,928✔
710
  *fma   = (char) ((ecx & bit_FMA) == bit_FMA);
92,928✔
711

712
  // work around a bug in GCC.
713
  // Ref to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77756
714
  __cpuid_fix(7u, eax, ebx, ecx, edx);
92,928✔
715
  *avx2 = (char) ((ebx & bit_AVX2) == bit_AVX2);
92,928✔
716
  *avx512 = (char)((ebx & bit_AVX512F) == bit_AVX512F);
92,928✔
717
#endif   // _TD_X86_
718
#endif
719

720
  return 0;
92,928✔
721
}
722

723
int32_t taosGetTotalMemory(int64_t *totalKB) {
95,317✔
724
  OS_PARAM_CHECK(totalKB);
95,317✔
725
#ifdef WINDOWS
726
  MEMORYSTATUSEX memsStat;
727
  memsStat.dwLength = sizeof(memsStat);
728
  if (!GlobalMemoryStatusEx(&memsStat)) {
729
    return TAOS_SYSTEM_WINAPI_ERROR(GetLastError());
730
  }
731

732
  *totalKB = memsStat.ullTotalPhys / 1024;
733
  return 0;
734
#elif defined(_TD_DARWIN_64)
735
  return 0;
736
#elif defined(TD_ASTRA) // TD_ASTRA_TODO
737
  *totalKB = (int64_t)256 * 1024;
738
  return 0;
739
#else
740
  *totalKB = (int64_t)(sysconf(_SC_PHYS_PAGES) * tsPageSizeKB);
95,314✔
741
  if(*totalKB <= 0) {
95,314!
742
    return TAOS_SYSTEM_ERROR(ERRNO);
×
743
  }
744
  return 0;
95,314✔
745
#endif
746
}
747

748
int32_t taosGetProcMemory(int64_t *usedKB) {
7✔
749
  OS_PARAM_CHECK(usedKB);
7✔
750
#ifdef WINDOWS
751
  unsigned bytes_used = 0;
752

753
#if defined(_WIN64) && defined(_MSC_VER)
754
  PROCESS_MEMORY_COUNTERS pmc;
755
  HANDLE                  cur_proc = GetCurrentProcess();
756

757
  if (GetProcessMemoryInfo(cur_proc, &pmc, sizeof(pmc))) {
758
    bytes_used = (unsigned)(pmc.WorkingSetSize + pmc.PagefileUsage);
759
  }
760
#endif
761

762
  *usedKB = bytes_used / 1024;
763
  return 0;
764
#elif defined(_TD_DARWIN_64) || defined(TD_ASTRA)
765
  *usedKB = 0;
766
  return 0;
767
#else
768
  TdFilePtr pFile = taosOpenFile(tsProcMemFile, TD_FILE_READ | TD_FILE_STREAM);
4✔
769
  if (pFile == NULL) {
4✔
770
    // printf("open file:%s failed", tsProcMemFile);
771
    return terrno;
3✔
772
  }
773

774
  ssize_t bytes = 0;
1✔
775
  char    line[1024] = {0};
1✔
776
  while (!taosEOFFile(pFile)) {
22!
777
    bytes = taosGetsFile(pFile, sizeof(line), line);
22✔
778
    if (bytes <= 0) {
22!
779
      break;
×
780
    }
781
    if (strstr(line, "VmRSS:") != NULL) {
22✔
782
      break;
1✔
783
    }
784
  }
785

786
  char tmp[10];
787
  (void)sscanf(line, "%s %" PRId64, tmp, usedKB);
1✔
788

789
  TAOS_SKIP_ERROR(taosCloseFile(&pFile));
1✔
790
  
791
  return 0;
1✔
792
#endif
793
}
794

795
int32_t taosGetSysAvailMemory(int64_t *availSize) {
10,932,816✔
796
#ifdef WINDOWS
797
  MEMORYSTATUSEX memsStat;
798
  memsStat.dwLength = sizeof(memsStat);
799
  if (!GlobalMemoryStatusEx(&memsStat)) {
800
    return -1;
801
  }
802

803
  int64_t nMemFree = memsStat.ullAvailPhys;
804
  int64_t nMemTotal = memsStat.ullTotalPhys;
805

806
  *availSize = nMemTotal - nMemFree;
807
  return 0;
808
#elif defined(_TD_DARWIN_64) || defined(TD_ASTRA)
809
  *availSize = 0;
810
  return 0;
811
#else
812
  TdFilePtr pFile = taosOpenFile("/proc/meminfo", TD_FILE_READ | TD_FILE_STREAM);
10,932,816✔
813
  if (pFile == NULL) {
10,932,816!
814
    return terrno;
×
815
  }
816

817
  ssize_t bytes = 0;
10,932,816✔
818
  char    line[128] = {0};
10,932,816✔
819
  int32_t expectedSize = 13; //"MemAvailable:"
10,932,816✔
820
  while (!taosEOFFile(pFile)) {
32,798,448!
821
    bytes = taosGetsFile(pFile, sizeof(line), line);
32,798,448✔
822
    if (bytes < 0) {
32,798,448!
823
      break;
×
824
    }
825
    if (line[0] != 'M' && line[3] != 'A') {
32,798,448!
826
      line[0] = 0;
×
827
      continue;
×
828
    }
829
    if (0 == strncmp(line, "MemAvailable:", expectedSize)) {
32,798,448✔
830
      break;
10,932,816✔
831
    }
832
  }
833

834
  if (0 == line[0]) {
10,932,816!
835
    return TSDB_CODE_UNSUPPORT_OS;
×
836
  }
837
  
838
  char tmp[32];
839
  (void)sscanf(line, "%s %" PRId64, tmp, availSize);
10,932,816✔
840

841
  *availSize *= 1024;
10,932,816✔
842
  
843
  (void)taosCloseFile(&pFile);
10,932,816✔
844
  return 0;
10,932,816✔
845
#endif
846
}
847

848
static void taosGetMemValue(char* line, char* key, int64_t* value){
60✔
849
  if(value == NULL) return;
60!
850
  *value = 0;
60✔
851
  if(line == NULL || line[0] == '\0') return;
60!
852

853
  char *colon_pos = strchr(line, ':');
60✔
854
  if (colon_pos != NULL) {
60!
855
    *colon_pos = '\0';
60✔
856
    if(sscanf(line, "%s", key) != 1){
60!
857
      key[0] = '\0';
×
858
    }
859
    if (sscanf(colon_pos + 1, "%" PRId64, value) != 1) {
60!
860
      *value = 0;
×
861
    }
862
  }
863
}
864

865
int32_t taosGetSysMemory(int64_t *usedKB, int64_t *freeKB, int64_t *cacheBufferKB) {
7✔
866
  OS_PARAM_CHECK(usedKB);
7✔
867
  OS_PARAM_CHECK(freeKB);
4!
868
  OS_PARAM_CHECK(cacheBufferKB);
4!
869
#ifdef WINDOWS
870
  MEMORYSTATUSEX memsStat;
871
  memsStat.dwLength = sizeof(memsStat);
872
  if (!GlobalMemoryStatusEx(&memsStat)) {
873
    return TAOS_SYSTEM_WINAPI_ERROR(GetLastError());
874
  }
875

876
  int64_t nMemFree = memsStat.ullAvailPhys / 1024;
877
  int64_t nMemTotal = memsStat.ullTotalPhys / 1024.0;
878

879
  *usedKB = nMemTotal - nMemFree;
880
  *freeKB = nMemFree;
881
  *cacheBufferKB = 0;
882
  return 0;
883
#elif defined(_TD_DARWIN_64) || defined(TD_ASTRA) // TD_ASTRA_TODO
884
  *usedKB = 0;
885
  *freeKB = 0;
886
  *cacheBufferKB = 0;
887
  return 0;
888
#else
889
  /*
890
  *usedKB = sysconf(_SC_AVPHYS_PAGES) * tsPageSizeKB;
891
  if(*usedKB <= 0) {
892
    return TAOS_SYSTEM_ERROR(ERRNO);
893
  }
894
  */
895
  TdFilePtr pFile = taosOpenFile("/proc/meminfo", TD_FILE_READ | TD_FILE_STREAM);
4✔
896
  if (pFile == NULL) {
4!
897
    return terrno;
×
898
  }
899

900
  char    line[1024] = {0};
4✔
901
  char    key[1024] = {0};
4✔
902
  int64_t  value = 0;
4✔
903
  ssize_t bytes = 0;
4✔
904

905
  //MemTotal
906
  int64_t total = 0;
4✔
907

908
  //MemFree
909
  int64_t mfree = 0;
4✔
910

911
  //MemAvailable
912
  int64_t available = 0;
4✔
913

914
  //Buffers
915
  int64_t buffer = 0;
4✔
916

917
  //Cached
918
  int64_t cached = 0;
4✔
919

920
  //SwapCached ,Active, Inactive, Active(anon), Inactive(anon), Active(file), Inactive(file), Unevictable, Mlocked, SwapTotal
921

922
  //SwapFree
923
  int64_t swapFree = 0;
4✔
924

925
  //Dirty, Writeback, AnonPages, Mapped, Shmem, KReclaimable, Slab
926

927
  //SReclaimable
928
  int64_t sReclaimable = 0;
4✔
929

930
  for(int32_t i=0; i < 30; i++){
124✔
931
    bytes = taosGetsFile(pFile, sizeof(line), line);
120✔
932
    if (bytes < 0) {
120!
933
      TAOS_SKIP_ERROR(taosCloseFile(&pFile));
×
934
      return terrno;
×
935
    }
936
    if (line[0] != 'M' && line[0] != 'B' && line[0] != 'C' && line[0] != 'S') {
120✔
937
      line[0] = 0;
60✔
938
      continue;
60✔
939
    }
940
    taosGetMemValue(line, key, &value);
60✔
941
    if(strncmp(key, "MemTotal", 1024) == 0) {total = value; continue;}
60✔
942
    if(strncmp(key, "MemFree", 1024) == 0) {mfree = value; continue;}
56✔
943
    if(strncmp(key, "MemAvailable", 1024) == 0) {available = value; continue;}
52✔
944
    if(strncmp(key, "Buffers", 1024) == 0) {buffer = value; continue;}
48✔
945
    if(strncmp(key, "Cached", 1024) == 0) {cached = value; continue;}
44✔
946
    if(strncmp(key, "SwapFree", 1024) == 0) {swapFree = value; continue;}
40✔
947
    if(strncmp(key, "SReclaimable", 1024) == 0) {sReclaimable = value; continue;}
36✔
948
  }
949

950
  //free   Unused memory (MemFree and SwapFree in /proc/meminfo)
951
  *freeKB = mfree;
4✔
952
  //buffers Memory used by kernel buffers (Buffers in /proc/meminfo)
953
  //cache  Memory used by the page cache and slabs (Cached and SReclaimable in /proc/meminfo)
954
  *cacheBufferKB = buffer + cached + sReclaimable;
4✔
955
  *usedKB = total - *freeKB - *cacheBufferKB;
4✔
956
  
957
  TAOS_SKIP_ERROR(taosCloseFile(&pFile));
4✔
958
  return 0;
4✔
959
#endif
960
}
961

962
int32_t taosGetDiskSize(char *dataDir, SDiskSize *diskSize) {
1,077,295✔
963
  OS_PARAM_CHECK(dataDir);
1,077,295✔
964
  OS_PARAM_CHECK(diskSize);
1,077,292✔
965
#if defined(WINDOWS)
966
  unsigned _int64 i64FreeBytesToCaller;
967
  unsigned _int64 i64TotalBytes;
968
  unsigned _int64 i64FreeBytes;
969

970
  BOOL fResult = GetDiskFreeSpaceExA(dataDir, (PULARGE_INTEGER)&i64FreeBytesToCaller, (PULARGE_INTEGER)&i64TotalBytes,
971
                                     (PULARGE_INTEGER)&i64FreeBytes);
972
  if (fResult) {
973
    diskSize->total = (int64_t)(i64TotalBytes);
974
    diskSize->avail = (int64_t)(i64FreeBytesToCaller);
975
    diskSize->used = (int64_t)(i64TotalBytes - i64FreeBytes);
976
    return 0;
977
  } else {
978
    // printf("failed to get disk size, dataDir:%s errno:%s", tsDataDir, strerror(ERRNO));
979
    terrno = TAOS_SYSTEM_WINAPI_ERROR(GetLastError());
980
    return terrno;
981
  }
982
#elif defined(_TD_DARWIN_64)
983
  struct statvfs info;
984
  if (statvfs(dataDir, &info)) {
985
    // printf("failed to get disk size, dataDir:%s errno:%s", tsDataDir, strerror(ERRNO));
986
    terrno = TAOS_SYSTEM_ERROR(ERRNO);
987
    return terrno;
988
  } else {
989
    diskSize->total = info.f_blocks * info.f_frsize;
990
    diskSize->avail = info.f_bavail * info.f_frsize;
991
    diskSize->used = (info.f_blocks - info.f_bfree) * info.f_frsize;
992
    return 0;
993
  }
994
#elif defined(TD_ASTRA)  // TD_ASTRA_TODO
995
  //  if (-1 == ioctl(dataDir, FIOFSTATVFSGETBYNAME, &info)) { // TODO:try to check whether the API is available
996
  //     terrno = TAOS_SYSTEM_ERROR(ERRNO);
997
  //     return terrno;
998
  diskSize->total = 100LL * 1024 * 1024 * 1024;
999
  diskSize->avail = 50LL * 1024 * 1024 * 1024;
1000
  diskSize->used = 50LL * 1024 * 1024 * 1024;
1001
  //  } else {
1002
  //    diskSize->total = info.f_blocks * info.f_frsize;
1003
  //    diskSize->avail = info.f_bavail * info.f_frsize;
1004
  //    diskSize->used = diskSize->total - diskSize->avail;
1005
  //  }
1006
  return 0;
1007
#else
1008
  struct statvfs info;
1009
  if (-1 == statvfs(dataDir, &info)) {
1,077,289!
1010
    terrno = TAOS_SYSTEM_ERROR(ERRNO);
×
1011
    return terrno;
×
1012
  } else {
1013
    diskSize->total = info.f_blocks * info.f_frsize;
1,077,290✔
1014
    diskSize->avail = info.f_bavail * info.f_frsize;
1,077,290✔
1015
    diskSize->used = diskSize->total - diskSize->avail;
1,077,290✔
1016
    
1017
    return 0;
1,077,290✔
1018
  }
1019
#endif
1020
}
1021

1022
int32_t taosGetProcIO(int64_t *rchars, int64_t *wchars, int64_t *read_bytes, int64_t *write_bytes) {
16✔
1023
  OS_PARAM_CHECK(rchars);
16✔
1024
  OS_PARAM_CHECK(wchars);
13✔
1025
  OS_PARAM_CHECK(read_bytes);
10✔
1026
  OS_PARAM_CHECK(write_bytes);
7✔
1027
#ifdef WINDOWS
1028
  IO_COUNTERS io_counter;
1029
  if (GetProcessIoCounters(GetCurrentProcess(), &io_counter)) {
1030
    *rchars = io_counter.ReadTransferCount;
1031
    *wchars = io_counter.WriteTransferCount;
1032
    *read_bytes = 0;
1033
    *write_bytes = 0;
1034
    return 0;
1035
  }
1036
  return TAOS_SYSTEM_WINAPI_ERROR(GetLastError());
1037
#elif defined(_TD_DARWIN_64) || defined(TD_ASTRA)
1038
  *rchars = 0;
1039
  *wchars = 0;
1040
  *read_bytes = 0;
1041
  *write_bytes = 0;
1042
  return 0;
1043
#else
1044
  TdFilePtr pFile = taosOpenFile(tsProcIOFile, TD_FILE_READ | TD_FILE_STREAM);
4✔
1045
  if (pFile == NULL) {
4✔
1046
    return terrno;
3✔
1047
  }
1048

1049
  ssize_t bytes = 0;
1✔
1050
  char    line[1024] = {0};
1✔
1051
  char    tmp[24];
1052
  int     readIndex = 0;
1✔
1053

1054
  while (!taosEOFFile(pFile)) {
6!
1055
    bytes = taosGetsFile(pFile, sizeof(line), line);
6✔
1056
    if (bytes < 10) {
6!
1057
      break;
×
1058
    }
1059
    if (strstr(line, "rchar:") != NULL) {
6✔
1060
      (void)sscanf(line, "%s %" PRId64, tmp, rchars);
1✔
1061
      readIndex++;
1✔
1062
    } else if (strstr(line, "wchar:") != NULL) {
5✔
1063
      (void)sscanf(line, "%s %" PRId64, tmp, wchars);
1✔
1064
      readIndex++;
1✔
1065
    } else if (strstr(line, "read_bytes:") != NULL) {  // read_bytes
4✔
1066
      (void)sscanf(line, "%s %" PRId64, tmp, read_bytes);
1✔
1067
      readIndex++;
1✔
1068
    } else if (strstr(line, "write_bytes:") != NULL) {  // write_bytes
3✔
1069
      (void)sscanf(line, "%s %" PRId64, tmp, write_bytes);
1✔
1070
      readIndex++;
1✔
1071
    } else {
1072
    }
1073

1074
    if (readIndex >= 4) break;
6✔
1075
  }
1076

1077
  TAOS_SKIP_ERROR(taosCloseFile(&pFile));
1✔
1078

1079
  if (readIndex < 4) {
1!
1080
    return -1;
×
1081
  }
1082

1083
  return 0;
1✔
1084
#endif
1085
}
1086

1087
int32_t taosGetProcIODelta(int64_t *rchars, int64_t *wchars, int64_t *read_bytes, int64_t *write_bytes) {
19✔
1088
  if (rchars == NULL || wchars == NULL || read_bytes == NULL || write_bytes == NULL) {
19✔
1089
    return TSDB_CODE_INVALID_PARA;
15✔
1090
  }
1091
  static int64_t last_rchars = -1;
1092
  static int64_t last_wchars = -1;
1093
  static int64_t last_read_bytes = -1;
1094
  static int64_t last_write_bytes = -1;
1095
  static int64_t cur_rchars = 0;
1096
  static int64_t cur_wchars = 0;
1097
  static int64_t cur_read_bytes = 0;
1098
  static int64_t cur_write_bytes = 0;
1099
  int32_t code = taosGetProcIO(&cur_rchars, &cur_wchars, &cur_read_bytes, &cur_write_bytes);
4✔
1100
  if (code == 0) {
4✔
1101
    if(last_rchars >=0 && last_wchars >=0 && last_read_bytes >=0 && last_write_bytes >= 0){
1!
1102
      *rchars = cur_rchars - last_rchars;
×
1103
      *wchars = cur_wchars - last_wchars;
×
1104
      *read_bytes = cur_read_bytes - last_read_bytes;
×
1105
      *write_bytes = cur_write_bytes - last_write_bytes;
×
1106
    }
1107
    else{
1108
      *rchars = 0;
1✔
1109
      *wchars = 0;
1✔
1110
      *read_bytes = 0;
1✔
1111
      *write_bytes = 0;
1✔
1112
    }
1113
    last_rchars = cur_rchars;
1✔
1114
    last_wchars = cur_wchars;
1✔
1115
    last_read_bytes = cur_read_bytes;
1✔
1116
    last_write_bytes = cur_write_bytes;
1✔
1117
  } else {
1118
    return code;
3✔
1119
  }
1120
  return 0;
1✔
1121
}
1122
void taosSetDefaultProcIODelta(int64_t *rchars, int64_t *wchars, int64_t *read_bytes, int64_t *write_bytes) {
×
1123
  if(rchars) *rchars = 0;
×
1124
  if(wchars) *wchars = 0;
×
1125
  if(read_bytes) *read_bytes = 0;
×
1126
  if(write_bytes) *write_bytes = 0;
×
1127
}
×
1128

1129
int32_t taosGetCardInfo(int64_t *receive_bytes, int64_t *transmit_bytes) {
10✔
1130
  OS_PARAM_CHECK(receive_bytes);
10✔
1131
  OS_PARAM_CHECK(transmit_bytes);
7✔
1132
  *receive_bytes = 0;
4✔
1133
  *transmit_bytes = 0;
4✔
1134

1135
#ifdef WINDOWS
1136
  return 0;
1137
#elif defined(_TD_DARWIN_64) || defined(TD_ASTRA)
1138
  return 0;
1139
#else
1140
  TdFilePtr pFile = taosOpenFile(tsSysNetFile, TD_FILE_READ | TD_FILE_STREAM);
4✔
1141
  if (pFile == NULL) {
4!
1142
    return terrno;
×
1143
  }
1144

1145
  ssize_t _bytes = 0;
4✔
1146
  char    line[1024];
1147

1148
  while (!taosEOFFile(pFile)) {
20!
1149
    int64_t o_rbytes = 0;
20✔
1150
    int64_t rpackts = 0;
20✔
1151
    int64_t o_tbytes = 0;
20✔
1152
    int64_t tpackets = 0;
20✔
1153
    int64_t nouse1 = 0;
20✔
1154
    int64_t nouse2 = 0;
20✔
1155
    int64_t nouse3 = 0;
20✔
1156
    int64_t nouse4 = 0;
20✔
1157
    int64_t nouse5 = 0;
20✔
1158
    int64_t nouse6 = 0;
20✔
1159
    char    nouse0[200] = {0};
20✔
1160

1161
    _bytes = taosGetsFile(pFile, sizeof(line), line);
20✔
1162
    if (_bytes <= 0) {
20✔
1163
      break;
4✔
1164
    }
1165

1166
    line[_bytes - 1] = 0;
16✔
1167

1168
    if (strstr(line, "lo:") != NULL) {
16✔
1169
      continue;
4✔
1170
    }
1171

1172
    (void)sscanf(line,
12✔
1173
           "%s %" PRId64 " %" PRId64 " %" PRId64 " %" PRId64 " %" PRId64 " %" PRId64 " %" PRId64 " %" PRId64 " %" PRId64
1174
           " %" PRId64,
1175
           nouse0, &o_rbytes, &rpackts, &nouse1, &nouse2, &nouse3, &nouse4, &nouse5, &nouse6, &o_tbytes, &tpackets);
1176
    *receive_bytes += o_rbytes;
12✔
1177
    *transmit_bytes += o_tbytes;
12✔
1178
  }
1179

1180
  TAOS_SKIP_ERROR(taosCloseFile(&pFile));
4✔
1181

1182
  return 0;
4✔
1183
#endif
1184
}
1185

1186
int32_t taosGetCardInfoDelta(int64_t *receive_bytes, int64_t *transmit_bytes) {
13✔
1187
  OS_PARAM_CHECK(receive_bytes);
13✔
1188
  OS_PARAM_CHECK(transmit_bytes);
7✔
1189
  static int64_t last_receive_bytes = -1;
1190
  static int64_t last_transmit_bytes = -1;
1191
  int64_t cur_receive_bytes = 0;
4✔
1192
  int64_t cur_transmit_bytes = 0;
4✔
1193
  int32_t code = taosGetCardInfo(&cur_receive_bytes, &cur_transmit_bytes);
4✔
1194
  if (code == 0) {
4!
1195
    if(last_receive_bytes >= 0 && last_transmit_bytes >= 0){
4!
1196
      *receive_bytes = cur_receive_bytes - last_receive_bytes;
×
1197
      *transmit_bytes = cur_transmit_bytes - last_transmit_bytes;
×
1198
    }
1199
    else{
1200
      *receive_bytes = 0;
4✔
1201
      *transmit_bytes = 0;
4✔
1202
    }
1203

1204
    last_receive_bytes = cur_receive_bytes;
4✔
1205
    last_transmit_bytes = cur_transmit_bytes;
4✔
1206
  } else {
1207
    return code;
×
1208
  }
1209
  return 0;
4✔
1210
}
1211
void taosSetDefaultCardInfoDelta(int64_t *receive_bytes, int64_t *transmit_bytes) {
×
1212
  if (receive_bytes) *receive_bytes = 0;
×
1213
  if (transmit_bytes) *transmit_bytes = 0;
×
1214
}
×
1215

1216
#if 0
1217
void taosKillSystem() {
1218
#ifdef WINDOWS
1219
  printf("function taosKillSystem, exit!");
1220
  exit(0);
1221
#elif defined(_TD_DARWIN_64) || defined(TD_ASTRA)
1222
  printf("function taosKillSystem, exit!");
1223
  exit(0);
1224
#else
1225
  // SIGINT
1226
  (void)printf("%sd will shut down soon", CUS_PROMPT);
1227
  (void)kill(tsProcId, 2);
1228
#endif
1229
}
1230
#endif
1231

1232
#define UUIDLEN (36)
1233
int32_t taosGetSystemUUIDLimit36(char *uid, int32_t uidlen) {
36,618✔
1234
  OS_PARAM_CHECK(uid);
36,618✔
1235
#ifdef WINDOWS
1236
  GUID guid;
1237
  HRESULT h = CoCreateGuid(&guid);
1238
  if (h != S_OK) {
1239
    return TAOS_SYSTEM_WINAPI_ERROR(GetLastError());
1240
  }
1241
  (void)snprintf(uid, uidlen, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", guid.Data1, guid.Data2, guid.Data3,
1242
           guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6],
1243
           guid.Data4[7]);
1244

1245
  return 0;
1246
#elif defined(_TD_DARWIN_64)
1247
  uuid_t uuid = {0};
1248
  char   buf[UUIDLEN37];
1249
  memset(buf, 0, UUIDLEN37);
1250
  uuid_generate(uuid);
1251
  // it's caller's responsibility to make enough space for `uid`, that's 36-char + 1-null
1252
  uuid_unparse_lower(uuid, buf);
1253
  (void)snprintf(uid, uidlen, "%.*s", (int)sizeof(buf), buf);
1254
  return 0;
1255
#elif defined(TD_ASTRA)
1256
  const char *template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";
1257
  const char *hex_chars = "0123456789abcdef";
1258
  int32_t     len = uidlen > 36 ? 36 : uidlen;
1259

1260
  for (int32_t i = 0; i < len; i++) {
1261
    if (template[i] == 'x') {
1262
      uid[i] = hex_chars[taosRand() & 15];
1263
    } else if (template[i] == 'y') {
1264
      uid[i] = hex_chars[(taosRand() & 3) + 8];  // 8, 9, a, or b
1265
    } else {
1266
      uid[i] = template[i];
1267
    }
1268
  }
1269
  if (len >= 0) {
1270
    uid[len] = 0;
1271
  }
1272

1273
  return 0;
1274
#else
1275
  int64_t len = 0;
36,615✔
1276

1277
  // fd = open("/proc/sys/kernel/random/uuid", 0);
1278
  TdFilePtr pFile = taosOpenFile("/proc/sys/kernel/random/uuid", TD_FILE_READ);
36,615✔
1279
  if (pFile == NULL) {
36,599!
1280
    return terrno;
×
1281
  } else {
1282
    len = taosReadFile(pFile, uid, uidlen);
36,599✔
1283
    TAOS_SKIP_ERROR(taosCloseFile(&pFile));
36,682✔
1284
    if (len < 0) {
36,694!
1285
      return terrno;
×
1286
    }
1287
  }
1288
  if (len >= UUIDLEN + 1) {
36,694✔
1289
    uid[len - 1] = 0;
35,225✔
1290
  } else {
1291
    uid[uidlen - 1] = 0;
1,469✔
1292
  }
1293

1294
  return 0;
36,694✔
1295
#endif
1296
}
1297

1298
int32_t taosGetSystemUUIDLen(char *uid, int32_t uidlen) {
1,460✔
1299
  if (uid == NULL || uidlen <= 0) {
1,460✔
1300
    return TSDB_CODE_APP_ERROR;
6✔
1301
  }
1302
  int num = (uidlen % UUIDLEN == 0) ? (uidlen / UUIDLEN) : (uidlen / UUIDLEN + 1);
1,454✔
1303
  int left = uidlen;
1,454✔
1304
  for (int i = 0; i < num; ++i) {
4,350✔
1305
    int32_t code = taosGetSystemUUIDLimit36(uid + i * UUIDLEN, left);
2,896✔
1306
    if (code != 0) {
2,896!
1307
      return code;
×
1308
    }
1309
    left -= UUIDLEN;
2,896✔
1310
  }
1311
  return TSDB_CODE_SUCCESS;
1,454✔
1312
}
1313

1314
char *taosGetCmdlineByPID(int pid) {
2,366✔
1315
#ifdef WINDOWS
1316
  return "";
1317
#elif defined(_TD_DARWIN_64)
1318
  static char cmdline[1024];
1319
  SET_ERRNO(0);
1320

1321
  if (proc_pidpath(pid, cmdline, sizeof(cmdline)) <= 0) {
1322
    fprintf(stderr, "PID is %d, %s", pid, strerror(ERRNO));
1323
    return strerror(ERRNO);
1324
  }
1325

1326
  return cmdline;
1327
#elif defined(TD_ASTRA)
1328
  return "";
1329
#else
1330
  static char cmdline[1024];
1331
  (void)snprintf(cmdline, sizeof(cmdline), "/proc/%d/cmdline", pid);
2,366✔
1332

1333
  // int fd = open(cmdline, O_RDONLY);
1334
  TdFilePtr pFile = taosOpenFile(cmdline, TD_FILE_READ);
2,366✔
1335
  if (pFile != NULL) {
2,366✔
1336
    int n = taosReadFile(pFile, cmdline, sizeof(cmdline) - 1);
2,352✔
1337
    if (n < 0) n = 0;
2,352✔
1338

1339
    if (n > 0 && cmdline[n - 1] == '\n') --n;
2,352!
1340

1341
    cmdline[n] = 0;
2,352✔
1342

1343
    TAOS_SKIP_ERROR(taosCloseFile(&pFile));
2,352✔
1344
  } else {
1345
    cmdline[0] = 0;
14✔
1346
  }
1347

1348
  return cmdline;
2,366✔
1349
#endif
1350
}
1351

1352
int64_t taosGetOsUptime() {
3,241✔
1353
#ifdef WINDOWS
1354
#elif defined(_TD_DARWIN_64) || defined(TD_ASTRA)
1355
#else
1356
  struct sysinfo info;
1357
  if (-1 == sysinfo(&info)) {
3,241!
1358
    terrno = TAOS_SYSTEM_ERROR(ERRNO);
×
1359
    return terrno;
×
1360
  }
1361
  
1362
  return (int64_t)info.uptime * 1000;
3,241✔
1363
  
1364
#endif
1365
  return 0;
1366
}
1367

1368
void taosSetCoreDump(bool enable) {
8,722✔
1369
  if (!enable) return;
8,722✔
1370
#ifdef WINDOWS
1371
  SetUnhandledExceptionFilter(exceptionHandler);
1372
  SetUnhandledExceptionFilter(&FlCrashDump);
1373
#elif defined(_TD_DARWIN_64) || defined(TD_ASTRA)
1374
#else
1375
  // 1. set ulimit -c unlimited
1376
  struct rlimit rlim;
1377
  struct rlimit rlim_new;
1378
  if (getrlimit(RLIMIT_CORE, &rlim) == 0) {
8,707!
1379
#ifndef _ALPINE
1380
    // printf("the old unlimited para: rlim_cur=%" PRIu64 ", rlim_max=%" PRIu64, rlim.rlim_cur, rlim.rlim_max);
1381
#else
1382
    // printf("the old unlimited para: rlim_cur=%llu, rlim_max=%llu", rlim.rlim_cur, rlim.rlim_max);
1383
#endif
1384
    rlim_new.rlim_cur = RLIM_INFINITY;
8,707✔
1385
    rlim_new.rlim_max = RLIM_INFINITY;
8,707✔
1386
    if (setrlimit(RLIMIT_CORE, &rlim_new) != 0) {
8,707!
1387
      // printf("set unlimited fail, error: %s", strerror(ERRNO));
1388
      rlim_new.rlim_cur = rlim.rlim_max;
×
1389
      rlim_new.rlim_max = rlim.rlim_max;
×
1390
      (void)setrlimit(RLIMIT_CORE, &rlim_new);
×
1391
    }
1392
  }
1393

1394
  if (getrlimit(RLIMIT_CORE, &rlim) == 0) {
8,707✔
1395
#ifndef _ALPINE
1396
    // printf("the new unlimited para: rlim_cur=%" PRIu64 ", rlim_max=%" PRIu64, rlim.rlim_cur, rlim.rlim_max);
1397
#else
1398
    // printf("the new unlimited para: rlim_cur=%llu, rlim_max=%llu", rlim.rlim_cur, rlim.rlim_max);
1399
#endif
1400
  }
1401

1402
#ifndef _TD_ARM_
1403
  // 2. set the path for saving core file
1404
  struct __sysctl_args args;
1405

1406
  int    old_usespid = 0;
8,707✔
1407
  size_t old_len = 0;
8,707✔
1408
  int    new_usespid = 1;
8,707✔
1409
  size_t new_len = sizeof(new_usespid);
8,707✔
1410

1411
  int name[] = {CTL_KERN, KERN_CORE_USES_PID};
8,707✔
1412

1413
  (void)memset(&args, 0, sizeof(struct __sysctl_args));
8,707✔
1414
  args.name = name;
8,707✔
1415
  args.nlen = sizeof(name) / sizeof(name[0]);
8,707✔
1416
  args.oldval = &old_usespid;
8,707✔
1417
  args.oldlenp = &old_len;
8,707✔
1418
  args.newval = &new_usespid;
8,707✔
1419
  args.newlen = new_len;
8,707✔
1420

1421
  old_len = sizeof(old_usespid);
8,707✔
1422

1423
#ifndef __loongarch64
1424
  if (syscall(SYS__sysctl, &args) == -1) {
8,707✔
1425
    // printf("_sysctl(kern_core_uses_pid) set fail: %s", strerror(ERRNO));
1426
  }
1427
#endif
1428

1429
  // printf("The old core_uses_pid[%" PRIu64 "]: %d", old_len, old_usespid);
1430

1431
  old_usespid = 0;
8,707✔
1432
  old_len = 0;
8,707✔
1433
  (void)memset(&args, 0, sizeof(struct __sysctl_args));
8,707✔
1434
  args.name = name;
8,707✔
1435
  args.nlen = sizeof(name) / sizeof(name[0]);
8,707✔
1436
  args.oldval = &old_usespid;
8,707✔
1437
  args.oldlenp = &old_len;
8,707✔
1438

1439
  old_len = sizeof(old_usespid);
8,707✔
1440

1441
#ifndef __loongarch64
1442
  if (syscall(SYS__sysctl, &args) == -1) {
8,707✔
1443
    // printf("_sysctl(kern_core_uses_pid) get fail: %s", strerror(ERRNO));
1444
  }
1445
#endif
1446

1447
  // printf("The new core_uses_pid[%" PRIu64 "]: %d", old_len, old_usespid);
1448
#endif
1449
#endif
1450
}
1451

1452
SysNameInfo taosGetSysNameInfo() {
6,825✔
1453
#ifdef WINDOWS
1454
  SysNameInfo info = {0};
1455
  DWORD       dwVersion = GetVersion();
1456

1457
  char *tmp = NULL;
1458
  tmp = getenv("OS");
1459
  if (tmp != NULL) tstrncpy(info.sysname, tmp, sizeof(info.sysname));
1460
  tmp = getenv("COMPUTERNAME");
1461
  if (tmp != NULL) tstrncpy(info.nodename, tmp, sizeof(info.nodename));
1462
  sprintf_s(info.release, sizeof(info.release), "%d", dwVersion & 0x0F);
1463
  sprintf_s(info.version, sizeof(info.release), "%d", (dwVersion >> 8) & 0x0F);
1464
  tmp = getenv("PROCESSOR_ARCHITECTURE");
1465
  if (tmp != NULL) tstrncpy(info.machine, tmp, sizeof(info.machine));
1466

1467
  return info;
1468
#elif defined(_TD_DARWIN_64)
1469
  SysNameInfo info = {0};
1470

1471
  struct utsname uts;
1472
  if (!uname(&uts)) {
1473
    tstrncpy(info.sysname, uts.sysname, sizeof(info.sysname));
1474
    tstrncpy(info.nodename, uts.nodename, sizeof(info.nodename));
1475
    tstrncpy(info.release, uts.release, sizeof(info.release));
1476
    tstrncpy(info.version, uts.version, sizeof(info.version));
1477
    tstrncpy(info.machine, uts.machine, sizeof(info.machine));
1478
  }
1479

1480
  char     localHostName[512];
1481
  TAOS_SKIP_ERROR(taosGetlocalhostname(localHostName, 512));
1482
  TdCmdPtr pCmd = taosOpenCmd("scutil --get LocalHostName");
1483
  tstrncpy(info.nodename, localHostName, sizeof(info.nodename));
1484

1485
  return info;
1486
#else
1487
  SysNameInfo info = {0};
6,825✔
1488
  struct utsname uts;
1489
  if (!uname(&uts)) {
6,825!
1490
    tstrncpy(info.sysname, uts.sysname, sizeof(info.sysname));
6,825✔
1491
    tstrncpy(info.nodename, uts.nodename, sizeof(info.nodename));
6,825✔
1492
    tstrncpy(info.release, uts.release, sizeof(info.release));
6,825✔
1493
    tstrncpy(info.version, uts.version, sizeof(info.version));
6,825✔
1494
    tstrncpy(info.machine, uts.machine, sizeof(info.machine));
6,825✔
1495
  } else {
1496
    terrno = TAOS_SYSTEM_ERROR(ERRNO);
×
1497
  }
1498

1499
  return info;
6,825✔
1500
#endif
1501
}
1502

1503
bool taosCheckCurrentInDll() {
2,095✔
1504
#ifdef WINDOWS
1505
  MEMORY_BASIC_INFORMATION mbi;
1506
  char                     path[PATH_MAX] = {0};
1507
  GetModuleFileName(
1508
      ((VirtualQuery(taosCheckCurrentInDll, &mbi, sizeof(mbi)) != 0) ? (HMODULE)mbi.AllocationBase : NULL), path,
1509
      PATH_MAX);
1510
  int strLastIndex = strlen(path);
1511
  if ((path[strLastIndex - 3] == 'd' || path[strLastIndex - 3] == 'D') &&
1512
      (path[strLastIndex - 2] == 'l' || path[strLastIndex - 2] == 'L') &&
1513
      (path[strLastIndex - 1] == 'l' || path[strLastIndex - 1] == 'L')) {
1514
    return true;
1515
  }
1516
  return false;
1517
#else
1518
  return false;
2,095✔
1519
#endif
1520
}
1521

1522
#ifdef _TD_DARWIN_64
1523
int32_t taosGetMaclocalhostnameByCommand(char *hostname, size_t maxLen) {
1524
  TdCmdPtr pCmd = taosOpenCmd("scutil --get LocalHostName");
1525
  if (pCmd != NULL) {
1526
    if (taosGetsCmd(pCmd, maxLen - 1, hostname) > 0) {
1527
      int len = strlen(hostname);
1528
      if (hostname[len - 1] == '\n') {
1529
        hostname[len - 1] = '\0';
1530
      }
1531
      return 0;
1532
    }
1533
    taosCloseCmd(&pCmd);
1534
  }
1535
  return TAOS_SYSTEM_ERROR(ERRNO);
1536
}
1537

1538
int32_t getMacLocalHostNameBySCD(char *hostname, size_t maxLen) {
1539
  SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR(""), NULL, NULL);
1540
  CFStringRef       hostname_cfstr = SCDynamicStoreCopyLocalHostName(store);
1541
  if (hostname_cfstr != NULL) {
1542
    CFStringGetCString(hostname_cfstr, hostname, maxLen - 1, kCFStringEncodingMacRoman);
1543
    CFRelease(hostname_cfstr);
1544
  } else {
1545
    return -1;
1546
  }
1547
  CFRelease(store);
1548
  return 0;
1549
}
1550
#endif
1551

1552
int32_t taosGetlocalhostname(char *hostname, size_t maxLen) {
10,043✔
1553
  OS_PARAM_CHECK(hostname);
10,043✔
1554
#ifdef _TD_DARWIN_64
1555
  int res = getMacLocalHostNameBySCD(hostname, maxLen);
1556
  if (res != 0) {
1557
    return taosGetMaclocalhostnameByCommand(hostname, maxLen);
1558
  } else {
1559
    return 0;
1560
  }
1561
#else
1562
  int r = gethostname(hostname, maxLen);
10,040✔
1563
  if (-1 == r) {
10,040!
1564
    terrno = TAOS_SYSTEM_ERROR(ERRNO);
×
1565
    return terrno;
×
1566
  }
1567
  return r;
10,040✔
1568
#endif
1569
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc