-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsimulator.c
324 lines (267 loc) · 6.28 KB
/
simulator.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
#include <assert.h>
#include <errno.h>
#include <getopt.h>
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include "buddy.h"
/**
* Various program statuses indicating success or failure of an operation
*/
typedef enum status_t {
SUCCESS = 0,
OUTOFMEMORY,
DOUBLEFREE,
BADINPUT
} status_t;
/**
* Severity of faults
*/
typedef enum severity_t {
ERROR,
WARNING
} severity_t;
/**
* Tracks a variable's pointer in memory and whether it is allocated
* or not
*/
typedef struct var_t {
void* mem; ///< A pointer to a memory block
bool in_use; ///< Is this variable currently in use? This is probably redundant if we assume variables not in use are NULL. For now just leave it as it is
} var_t;
static FILE *in = NULL; // Input file
static var_t var_map[256]; // Keep track of variable allocations
static int linenum = 0; // Line number in input file
/**
* Resolve a variable by name
*
* @param var Name of variable
* @return Returns a pointer to location of the variable's
* representation. Returns NULL if the var is not an alphabetic character.
*/
static var_t* get_var(char var)
{
if ((var >= 'a' && var <= 'z') || (var >= 'A' && var <= 'Z'))
return &var_map[(int) var];
else
return NULL;
}
/**
* Multi-purpose fault error message
*
* @param cmd A string representing a command
* @param msg A string that describes the nature of the fault
* @param sev Specify the severity of the fault
*/
static void print_fault(const char* cmd, const char* msg, severity_t sev)
{
const char* severity_msg;
switch(sev) {
case ERROR:
severity_msg = "ERROR";
break;
case WARNING:
severity_msg = "WARNING";
break;
default:
severity_msg = "?????";
}
fprintf(stderr, "%s: Line %d: %s\n", severity_msg, linenum, msg);
fprintf(stderr, " Faulting Command: %s\n", cmd);
}
/**
* Throw a parsing error
*
* @param cmd A string representing the faulting command
* @return Returns BADINPUT status
*/
static status_t parse_error(const char* cmd)
{
print_fault(cmd, "Failed to parse command", ERROR);
return BADINPUT;
}
/**
* Parses an allocation instruction
*
* @param cmd String representing an allocation command in the program
* @returns Status of read and execute
*/
static status_t parse_alloc(char* cmd)
{
assert(cmd != NULL);
assert(cmd[0] != '\0');
char var_name;
int size;
char alter_size;
int matched;
errno = 0;
matched = sscanf(cmd, "%c=alloc(%d%c)", &var_name, &size, &alter_size);
// Error check sprintf
if (matched == 3 && errno == 0) {
// Check what the alter_size variable actually contains
switch (alter_size) {
case 'k':
case 'K':
size *= 1024;
case ')':
break;
default:
return parse_error(cmd);
}
}
else {
return parse_error(cmd);
}
// Resolve variable
var_t* var = get_var(var_name);
if (var == NULL)
return parse_error(cmd);
// Allocate variable
var->mem = buddy_alloc(size);
if (var->mem == NULL) {
print_fault(cmd, "buddy_alloc returned NULL", WARNING);
printf("Out of memory\n");
return OUTOFMEMORY;
}
var->in_use = true;
return SUCCESS;
}
/**
* Parses a free instruction
*
* @param cmd String representing an allocation command in the program
* @returns Status of read and execute
*/
static status_t parse_free(char* cmd)
{
assert(cmd != NULL);
char var_name;
int matched;
var_t* var;
// Read the command string
errno = 0;
matched = sscanf(cmd, "free(%c)", &var_name);
// Check if sscanf was valid
if (matched != 1 || errno != 0 || (var = get_var(var_name)) == NULL)
return parse_error(cmd);
// Ensure that the variable is in use
if (!var->in_use) {
print_fault(cmd, "Double free", ERROR);
return DOUBLEFREE;
}
// Free variable
buddy_free(var->mem);
var->mem = NULL;
var->in_use = false;
return SUCCESS;
}
/**
* Simplify the command and call one of the sub parser functions
*
* @param cmd Raw command string. This parameter is mutated. This
* parameter cannot be NULL.
* @param cmd_len Length in bytes of the command string.
* @return Program status.
*/
static status_t parse_command(char* cmd, int cmd_len)
{
assert(cmd != NULL);
int ws_cursor = 0;
if (cmd[0] == '\0' || cmd[0] == '\n' || cmd[0] == '\r')
return SUCCESS;
// remove whitespace from command
for (int i = 0; i < cmd_len; ++i) {
switch (cmd[i]) {
case ' ':
case '\n':
case '\r':
case '\t':
break;
default:
cmd[ws_cursor++] = cmd[i];
}
}
status_t status;
// We have 2 commands: alloc and free.
if (strstr(cmd, "alloc") != NULL)
status = parse_alloc(cmd);
else if (strstr(cmd, "free") != NULL)
status = parse_free(cmd);
else
return parse_error(cmd);
if (status != SUCCESS)
return status;
// Output free blocks
buddy_dump();
return SUCCESS;
}
/**
* Feed each line of a file into the function parse_command
*
* @return Program status.
*/
static status_t parse_file()
{
char* line = NULL;
size_t len = 0;
ssize_t read;
status_t status = SUCCESS;
while (status == SUCCESS && (read = getline(&line, &len, in)) > 0) {
++linenum;
status = parse_command(line, len);
}
return status;
}
/**
* Output program manual
*
* @param prog_name Name of the program passed in as a command line argument.
* @param out File stream to write to.
*/
void print_usage(char* prog_name, FILE* out)
{
fprintf(out, "Usage:\n");
fprintf(out, " ./%s [-i filename]\n", prog_name);
fprintf(out, " -i [optional] - Specify an input file name to read from. If this option \n");
fprintf(out, " is not used then input is expected from standard input.\n");
}
int main(int argc, char** argv)
{
int opt;
status_t prog_status;
in = stdin;
// Parse command line options
while ((opt = getopt(argc, argv, "i:")) != -1) {
switch (opt) {
case 'i':
in = fopen(optarg, "r");
break;
case '?':
switch (optopt) {
case 'i':
fprintf(stderr, "ERROR: Missing filename after '%c'", optopt);
return EXIT_FAILURE;
}
print_usage(argv[0], stdout);
return EXIT_FAILURE;
}
}
// Error check the input file
if (in == NULL) {
perror("ERROR: Failed to open input file.");
return EXIT_FAILURE;
}
// Zero memory
memset(var_map, 0, sizeof(var_map));
// Execute program
buddy_init();
prog_status = parse_file();
if (in != stdin)
fclose(in);
if (prog_status == SUCCESS)
return EXIT_SUCCESS;
else
return EXIT_FAILURE;
}