PageRenderTime 25ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/tools/xenstat/libxenstat/src/xenstat_linux.c

https://gitlab.com/cardoe/xen
C | 501 lines | 391 code | 62 blank | 48 comment | 172 complexity | aafc26456b4a3057de30bedff7c2bfbc MD5 | raw file
  1. /* libxenstat: statistics-collection library for Xen
  2. * Copyright (C) International Business Machines Corp., 2005
  3. * Authors: Josh Triplett <josh@kernel.org>
  4. * Judy Fischbach <jfisch@cs.pdx.edu>
  5. * David Hendricks <cro_marmot@comcast.net>
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (at your option) any later version.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. */
  17. /*
  18. * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
  19. * Use is subject to license terms.
  20. */
  21. #include <fcntl.h>
  22. #include <dirent.h>
  23. #include <sys/types.h>
  24. #include <sys/stat.h>
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <string.h>
  28. #include <unistd.h>
  29. #include <regex.h>
  30. #include "xenstat_priv.h"
  31. #define SYSFS_VBD_PATH "/sys/bus/xen-backend/devices"
  32. struct priv_data {
  33. FILE *procnetdev;
  34. DIR *sysfsvbd;
  35. };
  36. static struct priv_data *
  37. get_priv_data(xenstat_handle *handle)
  38. {
  39. if (handle->priv != NULL)
  40. return handle->priv;
  41. handle->priv = malloc(sizeof(struct priv_data));
  42. if (handle->priv == NULL)
  43. return (NULL);
  44. ((struct priv_data *)handle->priv)->procnetdev = NULL;
  45. ((struct priv_data *)handle->priv)->sysfsvbd = NULL;
  46. return handle->priv;
  47. }
  48. /* Expected format of /proc/net/dev */
  49. static const char PROCNETDEV_HEADER[] =
  50. "Inter-| Receive |"
  51. " Transmit\n"
  52. " face |bytes packets errs drop fifo frame compressed multicast|"
  53. "bytes packets errs drop fifo colls carrier compressed\n";
  54. /* We need to get the name of the bridge interface for use with bonding interfaces */
  55. /* Use excludeName parameter to avoid adding bridges we don't care about, eg. virbr0 */
  56. void getBridge(char *excludeName, char *result, size_t resultLen)
  57. {
  58. struct dirent *de;
  59. DIR *d;
  60. char tmp[512] = { 0 };
  61. d = opendir("/sys/class/net");
  62. while ((de = readdir(d)) != NULL) {
  63. if ((strlen(de->d_name) > 0) && (de->d_name[0] != '.')
  64. && (strstr(de->d_name, excludeName) == NULL)) {
  65. sprintf(tmp, "/sys/class/net/%s/bridge", de->d_name);
  66. if (access(tmp, F_OK) == 0) {
  67. strncpy(result, de->d_name, resultLen - 1);
  68. result[resultLen - 1] = 0;
  69. }
  70. }
  71. }
  72. closedir(d);
  73. }
  74. /* parseNetLine provides regular expression based parsing for lines from /proc/net/dev, all the */
  75. /* information are parsed but not all are used in our case, ie. for xenstat */
  76. int parseNetDevLine(char *line, char *iface, unsigned long long *rxBytes, unsigned long long *rxPackets,
  77. unsigned long long *rxErrs, unsigned long long *rxDrops, unsigned long long *rxFifo,
  78. unsigned long long *rxFrames, unsigned long long *rxComp, unsigned long long *rxMcast,
  79. unsigned long long *txBytes, unsigned long long *txPackets, unsigned long long *txErrs,
  80. unsigned long long *txDrops, unsigned long long *txFifo, unsigned long long *txColls,
  81. unsigned long long *txCarrier, unsigned long long *txComp)
  82. {
  83. /* Temporary/helper variables */
  84. int ret;
  85. char *tmp;
  86. int i = 0, x = 0, col = 0;
  87. regex_t r;
  88. regmatch_t matches[19];
  89. int num = 19;
  90. /* Regular exception to parse all the information from /proc/net/dev line */
  91. char *regex = "([^:]*):([^ ]*)[ ]*([^ ]*)[ ]*([^ ]*)[ ]*([^ ]*)[ ]*([^ ]*)[ ]*([^ ]*)"
  92. "[ ]*([^ ]*)[ ]*([^ ]*)[ ]*([^ ]*)[ ]*([^ ]*)[ ]*([^ ]*)[ ]*([^ ]*)[ ]*"
  93. "([^ ]*)[ ]*([^ ]*)[ ]*([^ ]*)[ ]*([^ ]*)[ ]*([^ ]*)";
  94. /* Initialize all variables called has passed as non-NULL to zeros */
  95. if (iface != NULL)
  96. memset(iface, 0, sizeof(*iface));
  97. if (rxBytes != NULL)
  98. *rxBytes = 0;
  99. if (rxPackets != NULL)
  100. *rxPackets = 0;
  101. if (rxErrs != NULL)
  102. *rxErrs = 0;
  103. if (rxDrops != NULL)
  104. *rxDrops = 0;
  105. if (rxFifo != NULL)
  106. *rxFifo = 0;
  107. if (rxFrames != NULL)
  108. *rxFrames = 0;
  109. if (rxPackets != NULL)
  110. *rxPackets = 0;
  111. if (rxComp != NULL)
  112. *rxComp = 0;
  113. if (txBytes != NULL)
  114. *txBytes = 0;
  115. if (txPackets != NULL)
  116. *txPackets = 0;
  117. if (txErrs != NULL)
  118. *txErrs = 0;
  119. if (txDrops != NULL)
  120. *txDrops = 0;
  121. if (txFifo != NULL)
  122. *txFifo = 0;
  123. if (txColls != NULL)
  124. *txColls = 0;
  125. if (txCarrier != NULL)
  126. *txCarrier = 0;
  127. if (txComp != NULL)
  128. *txComp = 0;
  129. if ((ret = regcomp(&r, regex, REG_EXTENDED))) {
  130. regfree(&r);
  131. return ret;
  132. }
  133. tmp = (char *)malloc( sizeof(char) );
  134. if (regexec (&r, line, num, matches, REG_EXTENDED) == 0){
  135. for (i = 1; i < num; i++) {
  136. /* The expression matches are empty sometimes so we need to check it first */
  137. if (matches[i].rm_eo - matches[i].rm_so > 0) {
  138. /* Col variable contains current id of non-empty match */
  139. col++;
  140. tmp = (char *)realloc(tmp, (matches[i].rm_eo -
  141. matches[i].rm_so + 1) * sizeof(char));
  142. for (x = matches[i].rm_so; x < matches[i].rm_eo; x++)
  143. tmp[x - matches[i].rm_so] = line[x];
  144. /* We populate all the fields from /proc/net/dev line */
  145. if (i > 1) {
  146. unsigned long long ullTmp = strtoull(tmp, NULL, 10);
  147. switch (col) {
  148. case 2: if (rxBytes != NULL)
  149. *rxBytes = ullTmp;
  150. break;
  151. case 3: if (rxPackets != NULL)
  152. *rxPackets = ullTmp;
  153. break;
  154. case 4: if (rxErrs != NULL)
  155. *rxErrs = ullTmp;
  156. break;
  157. case 5: if (rxDrops != NULL)
  158. *rxDrops = ullTmp;
  159. break;
  160. case 6: if (rxFifo != NULL)
  161. *rxFifo = ullTmp;
  162. break;
  163. case 7: if (rxFrames != NULL)
  164. *rxFrames = ullTmp;
  165. break;
  166. case 8: if (rxComp != NULL)
  167. *rxComp = ullTmp;
  168. break;
  169. case 9: if (rxMcast != NULL)
  170. *rxMcast = ullTmp;
  171. break;
  172. case 10: if (txBytes != NULL)
  173. *txBytes = ullTmp;
  174. break;
  175. case 11: if (txPackets != NULL)
  176. *txPackets = ullTmp;
  177. break;
  178. case 12: if (txErrs != NULL)
  179. *txErrs = ullTmp;
  180. break;
  181. case 13: if (txDrops != NULL)
  182. *txDrops = ullTmp;
  183. break;
  184. case 14: if (txFifo != NULL)
  185. *txFifo = ullTmp;
  186. break;
  187. case 15: if (txColls != NULL)
  188. *txColls = ullTmp;
  189. break;
  190. case 16: if (txCarrier != NULL)
  191. *txCarrier = ullTmp;
  192. break;
  193. case 17: if (txComp != NULL)
  194. *txComp = ullTmp;
  195. break;
  196. }
  197. }
  198. else
  199. /* There were errors when parsing this directly in RE. strpbrk() helps */
  200. if (iface != NULL) {
  201. char *tmp2 = strpbrk(tmp, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
  202. if (tmp2 != NULL)
  203. strcpy(iface, tmp2);
  204. }
  205. memset(tmp, 0, matches[i].rm_eo - matches[i].rm_so);
  206. }
  207. }
  208. }
  209. free(tmp);
  210. regfree(&r);
  211. return 0;
  212. }
  213. /* Find out the domid and network number given an interface name.
  214. * Return 0 if the iface cannot be recognized as a Xen VIF. */
  215. static int get_iface_domid_network(const char *iface, unsigned int *domid_p, unsigned int *netid_p)
  216. {
  217. char nodename_path[48];
  218. FILE * nodename_file;
  219. int ret;
  220. snprintf(nodename_path, 48, "/sys/class/net/%s/device/nodename", iface);
  221. nodename_file = fopen(nodename_path, "r");
  222. if (nodename_file != NULL) {
  223. ret = fscanf(nodename_file, "backend/vif/%u/%u", domid_p, netid_p);
  224. fclose(nodename_file);
  225. if (ret == 2)
  226. return 1;
  227. }
  228. if (sscanf(iface, "vif%u.%u", domid_p, netid_p) == 2)
  229. return 1;
  230. return 0;
  231. }
  232. /* Collect information about networks */
  233. int xenstat_collect_networks(xenstat_node * node)
  234. {
  235. /* Helper variables for parseNetDevLine() function defined above */
  236. int i;
  237. char line[512] = { 0 }, iface[16] = { 0 }, devBridge[16] = { 0 }, devNoBridge[16] = { 0 };
  238. unsigned long long rxBytes, rxPackets, rxErrs, rxDrops, txBytes, txPackets, txErrs, txDrops;
  239. struct priv_data *priv = get_priv_data(node->handle);
  240. if (priv == NULL) {
  241. perror("Allocation error");
  242. return 0;
  243. }
  244. /* Open and validate /proc/net/dev if we haven't already */
  245. if (priv->procnetdev == NULL) {
  246. char header[sizeof(PROCNETDEV_HEADER)];
  247. priv->procnetdev = fopen("/proc/net/dev", "r");
  248. if (priv->procnetdev == NULL) {
  249. perror("Error opening /proc/net/dev");
  250. return 0;
  251. }
  252. /* Validate the format of /proc/net/dev */
  253. if (fread(header, sizeof(PROCNETDEV_HEADER) - 1, 1,
  254. priv->procnetdev) != 1) {
  255. perror("Error reading /proc/net/dev header");
  256. return 0;
  257. }
  258. header[sizeof(PROCNETDEV_HEADER) - 1] = '\0';
  259. if (strcmp(header, PROCNETDEV_HEADER) != 0) {
  260. fprintf(stderr,
  261. "Unexpected /proc/net/dev format\n");
  262. return 0;
  263. }
  264. }
  265. /* Fill in networks */
  266. /* FIXME: optimize this */
  267. fseek(priv->procnetdev, sizeof(PROCNETDEV_HEADER) - 1,
  268. SEEK_SET);
  269. /* We get the bridge devices for use with bonding interface to get bonding interface stats */
  270. getBridge("vir", devBridge, sizeof(devBridge));
  271. snprintf(devNoBridge, 16, "p%s", devBridge);
  272. while (fgets(line, 512, priv->procnetdev)) {
  273. xenstat_domain *domain;
  274. xenstat_network net;
  275. unsigned int domid;
  276. parseNetDevLine(line, iface, &rxBytes, &rxPackets, &rxErrs, &rxDrops, NULL, NULL, NULL,
  277. NULL, &txBytes, &txPackets, &txErrs, &txDrops, NULL, NULL, NULL, NULL);
  278. /* If the device parsed is network bridge and both tx & rx packets are zero, we are most */
  279. /* likely using bonding so we alter the configuration for dom0 to have bridge stats */
  280. if ((strstr(iface, devBridge) != NULL) &&
  281. (strstr(iface, devNoBridge) == NULL) &&
  282. ((domain = xenstat_node_domain(node, 0)) != NULL)) {
  283. for (i = 0; i < domain->num_networks; i++) {
  284. if ((domain->networks[i].id != 0) ||
  285. (domain->networks[i].tbytes != 0) ||
  286. (domain->networks[i].rbytes != 0))
  287. continue;
  288. domain->networks[i].tbytes = txBytes;
  289. domain->networks[i].tpackets = txPackets;
  290. domain->networks[i].terrs = txErrs;
  291. domain->networks[i].tdrop = txDrops;
  292. domain->networks[i].rbytes = rxBytes;
  293. domain->networks[i].rpackets = rxPackets;
  294. domain->networks[i].rerrs = rxErrs;
  295. domain->networks[i].rdrop = rxDrops;
  296. }
  297. }
  298. else /* Otherwise we need to preserve old behaviour */
  299. if (get_iface_domid_network(iface, &domid, &net.id)) {
  300. net.tbytes = txBytes;
  301. net.tpackets = txPackets;
  302. net.terrs = txErrs;
  303. net.tdrop = txDrops;
  304. net.rbytes = rxBytes;
  305. net.rpackets = rxPackets;
  306. net.rerrs = rxErrs;
  307. net.rdrop = rxDrops;
  308. /* FIXME: this does a search for the domid */
  309. domain = xenstat_node_domain(node, domid);
  310. if (domain == NULL) {
  311. fprintf(stderr,
  312. "Found interface vif%u.%u but domain %u"
  313. " does not exist.\n", domid, net.id,
  314. domid);
  315. continue;
  316. }
  317. if (domain->networks == NULL) {
  318. domain->num_networks = 1;
  319. domain->networks = malloc(sizeof(xenstat_network));
  320. } else {
  321. struct xenstat_network *tmp;
  322. domain->num_networks++;
  323. tmp = realloc(domain->networks,
  324. domain->num_networks *
  325. sizeof(xenstat_network));
  326. if (tmp == NULL)
  327. free(domain->networks);
  328. domain->networks = tmp;
  329. }
  330. if (domain->networks == NULL)
  331. return 0;
  332. domain->networks[domain->num_networks - 1] = net;
  333. }
  334. }
  335. return 1;
  336. }
  337. /* Free network information in handle */
  338. void xenstat_uninit_networks(xenstat_handle * handle)
  339. {
  340. struct priv_data *priv = get_priv_data(handle);
  341. if (priv != NULL && priv->procnetdev != NULL)
  342. fclose(priv->procnetdev);
  343. }
  344. static int read_attributes_vbd(const char *vbd_directory, const char *what, char *ret, int cap)
  345. {
  346. static char file_name[80];
  347. int fd, num_read;
  348. snprintf(file_name, sizeof(file_name), "%s/%s/%s",
  349. SYSFS_VBD_PATH, vbd_directory, what);
  350. fd = open(file_name, O_RDONLY, 0);
  351. if (fd==-1) return -1;
  352. num_read = read(fd, ret, cap - 1);
  353. close(fd);
  354. if (num_read<=0) return -1;
  355. ret[num_read] = '\0';
  356. return num_read;
  357. }
  358. /* Collect information about VBDs */
  359. int xenstat_collect_vbds(xenstat_node * node)
  360. {
  361. struct dirent *dp;
  362. struct priv_data *priv = get_priv_data(node->handle);
  363. if (priv == NULL) {
  364. perror("Allocation error");
  365. return 0;
  366. }
  367. if (priv->sysfsvbd == NULL) {
  368. priv->sysfsvbd = opendir(SYSFS_VBD_PATH);
  369. if (priv->sysfsvbd == NULL) {
  370. perror("Error opening " SYSFS_VBD_PATH);
  371. return 0;
  372. }
  373. }
  374. /* Get qdisk statistics */
  375. read_attributes_qdisk(node);
  376. rewinddir(priv->sysfsvbd);
  377. for(dp = readdir(priv->sysfsvbd); dp != NULL ;
  378. dp = readdir(priv->sysfsvbd)) {
  379. xenstat_domain *domain;
  380. xenstat_vbd vbd;
  381. unsigned int domid;
  382. int ret;
  383. char buf[256];
  384. ret = sscanf(dp->d_name, "%3s-%u-%u", buf, &domid, &vbd.dev);
  385. if (ret != 3)
  386. continue;
  387. if (strcmp(buf,"vbd") == 0)
  388. vbd.back_type = 1;
  389. else if (strcmp(buf,"tap") == 0)
  390. vbd.back_type = 2;
  391. else
  392. continue;
  393. domain = xenstat_node_domain(node, domid);
  394. if (domain == NULL) {
  395. fprintf(stderr,
  396. "Found interface %s-%u-%u but domain %u"
  397. " does not exist.\n",
  398. buf, domid, vbd.dev, domid);
  399. continue;
  400. }
  401. if((read_attributes_vbd(dp->d_name, "statistics/oo_req", buf, 256)<=0)
  402. || ((ret = sscanf(buf, "%llu", &vbd.oo_reqs)) != 1))
  403. {
  404. continue;
  405. }
  406. if((read_attributes_vbd(dp->d_name, "statistics/rd_req", buf, 256)<=0)
  407. || ((ret = sscanf(buf, "%llu", &vbd.rd_reqs)) != 1))
  408. {
  409. continue;
  410. }
  411. if((read_attributes_vbd(dp->d_name, "statistics/wr_req", buf, 256)<=0)
  412. || ((ret = sscanf(buf, "%llu", &vbd.wr_reqs)) != 1))
  413. {
  414. continue;
  415. }
  416. if((read_attributes_vbd(dp->d_name, "statistics/rd_sect", buf, 256)<=0)
  417. || ((ret = sscanf(buf, "%llu", &vbd.rd_sects)) != 1))
  418. {
  419. continue;
  420. }
  421. if((read_attributes_vbd(dp->d_name, "statistics/wr_sect", buf, 256)<=0)
  422. || ((ret = sscanf(buf, "%llu", &vbd.wr_sects)) != 1))
  423. {
  424. continue;
  425. }
  426. if ((xenstat_save_vbd(domain, &vbd)) == NULL) {
  427. perror("Allocation error");
  428. return 0;
  429. }
  430. }
  431. return 1;
  432. }
  433. /* Free VBD information in handle */
  434. void xenstat_uninit_vbds(xenstat_handle * handle)
  435. {
  436. struct priv_data *priv = get_priv_data(handle);
  437. if (priv != NULL && priv->sysfsvbd != NULL)
  438. closedir(priv->sysfsvbd);
  439. }