1 /************************************************************************
2 Copyright (c) 2015, The Linux Foundation. All rights reserved.
3 
4 Redistribution and use in source and binary forms, with or without
5 modification, are permitted provided that the following conditions are
6 met:
7     * Redistributions of source code must retain the above copyright
8       notice, this list of conditions and the following disclaimer.
9     * Redistributions in binary form must reproduce the above
10       copyright notice, this list of conditions and the following
11       disclaimer in the documentation and/or other materials provided
12       with the distribution.
13     * Neither the name of The Linux Foundation nor the names of its
14       contributors may be used to endorse or promote products derived
15       from this software without specific prior written permission.
16 
17 THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
18 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
20 ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
21 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26 OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 ************************************************************************/
29 
30 /**
31  * @file datatop_meminfo_file_poll.c
32  * @brief Adds ability for data collection from /proc/meminfo
33  *
34  * File contains methods for searching and polling data from
35  * "/proc/meminfo"
36  */
37 
38 #include <stdio.h>
39 #include <stdint.h>
40 #include <string.h>
41 #include <stdlib.h>
42 #include <inttypes.h>
43 #include "datatop_interface.h"
44 #include "datatop_fileops.h"
45 #include "datatop_str.h"
46 
47 #define DTOP_MEM_SIZE 8192
48 #define DTOP_MEM_LINE (DTOP_MEM_SIZE>>2)
49 
50 /**
51 * @struct dtop_meminfo_vars
52 * @brief Struct used to hold necessary variables for /proc/meminfo dpg
53 *
54 * @var dtop_meminfo_vars::line
55 * Array of strings where necessary dp names and values are held.
56 * @var dtop_meminfo_vars::line_count
57 * Number of lines the file is that the dpg represents.
58 */
59 struct dtop_meminfo_vars {
60 	char **line;
61 	int line_count;
62 };
63 
64 /**
65  * @brief Parses lines with data in "/proc/meminfo"
66  *
67  * @param line1 Line to parse to find datapoint names and values.
68  * @param len1 Length of line1.
69  * @param l Index in the dictionary the key/value pair is added to.
70  * @param dict Dictionary the keys and values are added to.
71  */
dt_meminfo_parse(char * line1,int len1,int l,struct dt_procdict * dict)72 int dt_meminfo_parse(char *line1, int len1,
73 			int l, struct dt_procdict *dict)
74 {
75 	int i, k, n;
76 	if (len1 < 1)
77 		return 0;
78 
79 	if (line1 == 0 || dict == 0)
80 		return 0;
81 
82 	k = l;
83 	dict->key[k] = &line1[0];
84 	for (i = 0; i < len1 && k < DTOP_DICT_SIZE; i++) {
85 		if (line1[i] == ' ' || line1[i] == '	') {
86 			line1[i] = 0;
87 			n = i+1;
88 			while (line1[n] == '	' || line1[n] == ' ')
89 				n++;
90 			dict->val[k] = &line1[n];
91 			while (line1[n] != ' ')
92 				n++;
93 			line1[n] = 0;
94 			break;
95 		}
96 	}
97 	k++;
98 	dict->max = k;
99 	return k;
100 }
101 
102 /**
103  * @brief Stores the data collected from a "/proc/meminfo"
104  *
105  * @param dpg Struct that polled data is added to.
106  * @return DTOP_POLL_IO_ERR - Poll of dpg unsuccessful.
107  * @return DTOP_POLL_OK - Poll of dpg successful.
108  */
dtop_meminfo_poll(struct dtop_data_point_gatherer * dpg)109 int dtop_meminfo_poll(struct dtop_data_point_gatherer *dpg)
110 {
111 	char *data;
112 	int *line_len = malloc(sizeof(int) *
113 			((struct dtop_meminfo_vars *)
114 			(dpg->priv))->line_count);
115 	int read;
116 	struct dt_procdict dict;
117 	int i, j, n, sum;
118 
119 	read = dt_read_file(dpg->file, &data, DTOP_MEM_SIZE);
120 	if (read == 0 || data == 0)
121 		return DTOP_POLL_IO_ERR;
122 
123 	sum = 0;
124 	/* Assigns each line read from the file, a length */
125 	for (n = 0; n < ((struct dtop_meminfo_vars *)
126 				(dpg->priv))->line_count; n++) {
127 		line_len[n] = dt_read_line(((struct dtop_meminfo_vars *)
128 					(dpg->priv))->line[n],
129 					   DTOP_MEM_LINE, data,
130 					   DTOP_MEM_SIZE, sum);
131 		if (n <= (((struct dtop_meminfo_vars *)
132 			(dpg->priv))->line_count - 1)) {
133 			sum += (line_len[n] + 1);
134 		}
135 
136 	}
137 
138 	/* Stores dp names and values in dictionary */
139 	for (i = 0; i < dpg->data_points_len; i++)
140 		dt_meminfo_parse(((struct dtop_meminfo_vars *)
141 			(dpg->priv))->line[i], line_len[i], i, &dict);
142 
143 	/* Assigns the dp value to the dp struct */
144 	for (j = 0; j < dpg->data_points_len; j++) {
145 		i = dt_find_dict_idx(dpg->data_points[j].name, &dict);
146 		if (i >= 0 && i < dict.max) {
147 			sscanf(dict.val[i], "%" PRIu64,
148 			       &(dpg->data_points[i].data.d_ulong));
149 			dpg->data_points[i].data.d_ulong *= 1024;
150 			if (dpg->data_points[i].
151 				initial_data_populated == NOT_POPULATED) {
152 				dpg->data_points[i].initial_data.d_ulong
153 					= dpg->data_points[i].data.d_ulong;
154 				dpg->data_points[i].initial_data_populated
155 					= POPULATED;
156 			}
157 		}
158 	}
159 
160 	dt_free(&data);
161 	free(line_len);
162 	return DTOP_POLL_OK;
163 }
164 
165 /**
166  * @brief Frees dynamically allocated "/proc/meminfo" dpg.
167  *
168  * Frees the memory of the dpg along with it's data_points
169  * and other malloc'd memory no longer needed.
170  *
171  * @param dpg Dpg to deconstruct and deallocate memory for.
172  */
dtop_meminfo_dpg_deconstructor(struct dtop_data_point_gatherer * dpset)173 static void dtop_meminfo_dpg_deconstructor
174 			(struct dtop_data_point_gatherer *dpset)
175 {
176 	int i;
177 	free(dpset->data_points);
178 	for (i = 0; i < ((struct dtop_meminfo_vars *)
179 				(dpset->priv))->line_count; i++)
180 		free(((struct dtop_meminfo_vars *)(dpset->priv))->line[i]);
181 	free(((struct dtop_meminfo_vars *)(dpset->priv))->line);
182 	free(((struct dtop_meminfo_vars *)(dpset->priv)));
183 	free(dpset);
184 }
185 
186 /**
187  * @brief Creates a dpg for "/proc/meminfo" file
188  *
189  * Dynamically allocates memory for dpg which is then added to a linked list
190  * via the dtop_register(dpg) function call.
191  *
192  * @param data_points dtop_data_point struct that dpg points to.
193  * @param storage dtop_meminfo_vars struct that holds relevant dpg variables.
194  */
construct_meminfo_file_dpg(struct dtop_data_point * data_points,struct dtop_meminfo_vars * storage)195 static void construct_meminfo_file_dpg(struct dtop_data_point
196 		*data_points, struct dtop_meminfo_vars *storage)
197 {
198 	struct dtop_data_point_gatherer *dpg = malloc
199 		(sizeof(struct dtop_data_point_gatherer));
200 	dpg->prefix = "/proc/meminfo";
201 	dpg->file = "/proc/meminfo";
202 	dpg->poll = dtop_meminfo_poll;
203 	dpg->data_points = data_points;
204 	dpg->priv = (struct dtop_meminfo_vars *)storage;
205 	dpg->data_points_len = storage->line_count;
206 	dpg->deconstruct = dtop_meminfo_dpg_deconstructor;
207 
208 	dtop_register(dpg);
209 }
210 
211 /**
212  * @brief Scans "/proc/meminfo in order to autodetect dps.
213  *
214  * Searches through "/proc/meminfo" file for all available data
215  * points to create as dp structs.
216  *
217  * @param storage dtop_meminfo_vars struct where relevant variables are stored.
218  */
dtop_meminfo_search(struct dtop_meminfo_vars * storage)219 int dtop_meminfo_search(struct dtop_meminfo_vars *storage)
220 {
221 	int i, k, n, sum;
222 	char *data;
223 	int *line_len = malloc(sizeof(int) * storage->line_count);
224 	int read;
225 	struct dt_procdict dict;
226 	struct dtop_data_point *data_points;
227 
228 	storage->line = malloc(storage->line_count * sizeof(*storage->line));
229 
230 	for (i = 0; i < storage->line_count; i++)
231 		storage->line[i] = malloc(sizeof(char) * DTOP_MEM_LINE);
232 
233 	read = dt_read_file("/proc/meminfo", &data, DTOP_MEM_SIZE);
234 	if (read == 0 || data == 0)
235 		return DTOP_POLL_IO_ERR;
236 
237 	sum = 0;
238 	/* Assigns each line read from the file, a length */
239 	for (n = 0; n < storage->line_count; n++) {
240 		line_len[n] = dt_read_line(storage->line[n],
241 					   DTOP_MEM_LINE, data,
242 					   DTOP_MEM_SIZE, sum);
243 		if (n < (storage->line_count - 1))
244 			sum += (line_len[n] + 1);
245 	}
246 
247 	/* Stores dp names in dictionary */
248 	for (i = 0; i < (storage->line_count); i++)
249 		dt_parse_proc_same_line_key_and_val(storage->line[i],
250 						line_len[i], i, &dict);
251 
252 	data_points = malloc
253 		       (storage->line_count * sizeof(struct dtop_data_point));
254 
255 	k = 0;
256 	/* Creates a dtop_data_point struct for each dp found in the file */
257 	for (i = 0; i < dict.max; i++) {
258 		data_points[i].name = dict.key[i];
259 		data_points[i].prefix = NULL;
260 		data_points[i].type = DTOP_ULONG;
261 		k++;
262 	}
263 
264 	/* Calls dpg constructor, dpg will point to the dp struct */
265 	construct_meminfo_file_dpg(data_points, storage);
266 
267 	free(line_len);
268 	dt_free(&data);
269 
270 	return DTOP_POLL_OK;
271 }
272 
273 /**
274  * @brief Calls dtop_search for "/proc/meminfo" file.
275  */
dtop_meminfo_init(void)276 void dtop_meminfo_init(void)
277 {
278 	struct dtop_meminfo_vars *storage = malloc
279 			(sizeof(struct dtop_meminfo_vars));
280 	storage->line_count = dtop_get_file_line_amount("/proc/meminfo");
281 	dtop_meminfo_search(storage);
282 }
283