]> rtime.felk.cvut.cz Git - jailhouse.git/blob - inmates/demos/x86/Membench.c
jailhouse: inmates: bench: Add -R option -- repeats count.
[jailhouse.git] / inmates / demos / x86 / Membench.c
1 #ifdef JAILHOUSE\r
2 \r
3 #include <inmate.h>\r
4 \r
5 \r
6 #define CMDLINE_BUFFER_SIZE     256\r
7 CMDLINE_BUFFER(CMDLINE_BUFFER_SIZE);\r
8 \r
9 #define POLLUTE_CACHE_SIZE      (512 * 1024)\r
10 \r
11 #ifdef CONFIG_UART_OXPCIE952\r
12 #define UART_BASE               0xe010\r
13 #else\r
14 #define UART_BASE               0x3f8\r
15 #endif\r
16 #define UART_LSR                0x5\r
17 #define UART_LSR_THRE           0x20\r
18 #define UART_IDLE_LOOPS         100\r
19 \r
20 //uintstd in jailhouse way\r
21 #define uint32_t u32\r
22 #define uint64_t u64\r
23 \r
24 #define perror(FUNC) (printk(#FUNC))\r
25 \r
26 #define printf printk\r
27 \r
28 #define exit(SIG) { printk("exit with %d\n", SIG); asm volatile("hlt");}\r
29 \r
30 //-----Time-and-and-randomization-overrides-------------------------------\r
31 static inline unsigned long time(unsigned long * seconds)\r
32 {\r
33          u64 time_sec = tsc_read();\r
34          if (seconds != NULL){\r
35                 (*seconds) = time_sec;\r
36          }\r
37          return time_sec;\r
38 }\r
39 \r
40 /*\r
41 *       Tables of Maximally-Equidistributed Combined Lfsr Generators (1998)\r
42 *       by Pierre L'Ecuyer\r
43 *       taken from: http://stackoverflow.com/questions/1167253/implementation-of-rand\r
44 */\r
45 static unsigned int z1 = 12345, z2 = 12345, z3 = 12345, z4 = 12345;\r
46 \r
47 static unsigned int lfsr113_Bits (void)\r
48 {\r
49    unsigned int b;\r
50    b  = ((z1 << 6) ^ z1) >> 13;\r
51    z1 = ((z1 & 4294967294U) << 18) ^ b;\r
52    b  = ((z2 << 2) ^ z2) >> 27;\r
53    z2 = ((z2 & 4294967288U) << 2) ^ b;\r
54    b  = ((z3 << 13) ^ z3) >> 21;\r
55    z3 = ((z3 & 4294967280U) << 7) ^ b;\r
56    b  = ((z4 << 3) ^ z4) >> 12;\r
57    z4 = ((z4 & 4294967168U) << 13) ^ b;\r
58    return (z1 ^ z2 ^ z3 ^ z4);\r
59 }\r
60 \r
61 static unsigned int rand(void)\r
62 {\r
63         return lfsr113_Bits();\r
64 }\r
65 \r
66 static void srand(unsigned int seed)\r
67 {\r
68    //seed into z1 z2 z3 z4\r
69    z1 = z2 = z3 = z4 = seed;\r
70 }\r
71 //-END:-Time-and-and-randomization-overrides-------------------------------\r
72 \r
73 //-----Assertion-overrides-------------------------------------------------\r
74 #ifdef NDEBUG\r
75 # define assert(EX)\r
76 #else\r
77 # define assert(EX) (void)((EX) || (__assert (#EX, __FILE__, __LINE__),0))\r
78 #endif\r
79 \r
80 static inline void __assert(const char *msg, const char *file, int line)\r
81 {\r
82         printk("Assertion %s in %s:%s failed.\n", msg, file, line);\r
83         exit(-1);\r
84 }\r
85 //-END:-Assertion-overrides-------------------------------------------------\r
86 \r
87 //------Threads-overrides---------------------------------------------------\r
88  typedef struct\r
89  {\r
90          u8 count;\r
91          u8 bits;\r
92  }cpu_set_t;\r
93 \r
94 typedef u32 pthread_t;\r
95 typedef u32 pthread_barrier_t;\r
96 typedef u32 pthread_attr_t;\r
97 \r
98 static inline void CPU_ZERO(cpu_set_t *set) { set->bits = 0; set->count = 0; }\r
99 static inline void CPU_SET(int cpu, cpu_set_t *set) { set->bits |= (1 << cpu); set->count++; }\r
100 static inline void CPU_CLR(int cpu, cpu_set_t *set) { set->bits ^= (1 << cpu); set->count--; }\r
101 static inline int  CPU_ISSET(int cpu, cpu_set_t *set) { return (set->bits & (1 << cpu)); }\r
102 static inline int CPU_COUNT(cpu_set_t *set) { return (int) set->count; }\r
103 \r
104 static inline int pthread_setaffinity_np(pthread_t id, unsigned long affty, cpu_set_t * set) {return 0;}\r
105 \r
106 static inline void pthread_barrier_init(pthread_barrier_t * bar, pthread_attr_t * attr, unsigned count) {}\r
107 static inline void pthread_barrier_wait(pthread_barrier_t * bar) {}\r
108 static inline void pthread_barrier_destroy(pthread_barrier_t * bar) {}\r
109 static inline void pthread_join(pthread_t id, void * smth){}\r
110 \r
111 static int pthread_create(pthread_t *thread, const pthread_attr_t *attr,\r
112                           void *(*start_routine) (void *), void *arg)\r
113 {\r
114         start_routine(arg);\r
115         return 0;\r
116 }\r
117 \r
118 //-END:-Threads-overrides---------------------------------------------------\r
119 \r
120 //just remove fflush\r
121 #define fflush (void)sizeof\r
122 #define stdout 0\r
123 #define stdin 0\r
124 #define stderr 0\r
125 \r
126 #else //END JAILHOUSE\r
127 \r
128 #define _GNU_SOURCE\r
129 #include <assert.h>\r
130 #include <pthread.h>\r
131 #include <sched.h>\r
132 #include <stdbool.h>\r
133 #include <stdint.h>\r
134 #include <stdio.h>\r
135 #include <stdlib.h>\r
136 #include <string.h>\r
137 #include <sys/types.h>\r
138 #include <time.h>\r
139 #include <unistd.h>\r
140 \r
141 #endif //END Linux\r
142 \r
143 #define STRINGIFY(val) #val\r
144 #define TOSTRING(val) STRINGIFY(val)\r
145 #define LOC __FILE__ ":" TOSTRING(__LINE__) ": "\r
146 \r
147 #define CHECK(cmd) ({ int ret = (cmd); if (ret == -1) { perror(LOC #cmd); exit(1); }; ret; })\r
148 #define CHECKPTR(cmd) ({ void *ptr = (cmd); if (ptr == (void*)-1) { perror(LOC #cmd); exit(1); }; ptr; })\r
149 #define CHECKNULL(cmd) ({ typeof(cmd) ptr = (cmd); if (ptr == NULL) { perror(LOC #cmd); exit(1); }; ptr; })\r
150 //#define CHECKFGETS(s, size, stream) ({ void *ptr = fgets(s, size, stream); if (ptr == NULL) { if (feof(stream)) fprintf(stderr, LOC "fgets(" #s "): Unexpected end of stream\n"); else perror(LOC "fgets(" #s ")"); exit(1); }; ptr; })\r
151 #define CHECKTRUE(bool, msg) ({ if (!(bool)) { printf("Error: " msg "\n"); exit(1); }; })\r
152 \r
153 struct cfg {\r
154         bool sequential;\r
155         unsigned size;\r
156         unsigned num_threads;\r
157         unsigned read_count;\r
158         cpu_set_t cpu_set;\r
159         bool write;\r
160         unsigned ofs;\r
161         bool use_cycles; /* instead of ns */\r
162         unsigned repeats;\r
163 };\r
164 \r
165 struct s {\r
166         struct s *ptr;\r
167         uint32_t dummy[(64 - sizeof(struct s*))/sizeof(uint32_t)];\r
168 };\r
169 \r
170 _Static_assert(sizeof(struct s) == 64, "Struct size differs from cacheline size");\r
171 \r
172 #ifdef JAILHOUSE\r
173 #define MAX_CPUS 1\r
174 #else\r
175 #define MAX_CPUS 8\r
176 #endif\r
177 \r
178 #ifdef __aarch64__\r
179 #define MRS32(reg) ({ uint32_t v; asm volatile ("mrs %0," # reg : "=r" (v)); v; })\r
180 #define MRS64(reg) ({ uint64_t v; asm volatile ("mrs %0," # reg : "=r" (v)); v; })\r
181 \r
182 #define MSR(reg, v) ({ asm volatile ("msr " # reg ",%0" :: "r" (v)); })\r
183 \r
184 static void ccntr_init(void)\r
185 {\r
186         MSR(PMCNTENSET_EL0, 0x80000000);\r
187         MSR(PMCR_EL0, MRS32(PMCR_EL0) | 1);\r
188 }\r
189 \r
190 static uint64_t ccntr_get(void)\r
191 {\r
192         return MRS64(PMCCNTR_EL0);\r
193 }\r
194 #else\r
195 static void ccntr_init(void) {}\r
196 \r
197 static uint64_t ccntr_get(void)\r
198 {\r
199 #ifdef JAILHOUSE\r
200 //taken from lib/timing.c\r
201 #ifdef __x86_64__\r
202         u32 lo, hi;\r
203 \r
204         asm volatile("rdtsc" : "=a" (lo), "=d" (hi));\r
205         return (u64)lo | (((u64)hi) << 32);\r
206 #else\r
207         u64 v;\r
208 \r
209         asm volatile("rdtsc" : "=A" (v));\r
210         return v;\r
211 #endif\r
212 \r
213 #else\r
214         return 0;\r
215 #endif\r
216 }\r
217 #endif\r
218 \r
219 static uint64_t get_time(struct cfg *cfg)\r
220 {\r
221         if (cfg->use_cycles == false) {\r
222 \r
223 #ifdef JAILHOUSE\r
224                 return tsc_read();\r
225 #else\r
226         struct timespec t;\r
227 \r
228         clock_gettime(CLOCK_MONOTONIC, &t);\r
229         return (uint64_t)t.tv_sec * 1000000000 + t.tv_nsec;\r
230 #endif\r
231 \r
232         } else {\r
233                 return ccntr_get();\r
234         }\r
235 }\r
236 \r
237 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))\r
238 \r
239 static void prepare(struct s *array, unsigned size, bool sequential)\r
240 {\r
241         int i, j;\r
242         int count = size / sizeof(struct s);\r
243 \r
244         if (sequential) {\r
245                 for (i = 0; i < count - 1; i++){\r
246                         array[i].ptr = &array[i+1];\r
247                 }\r
248                 array[count - 1].ptr = &array[0];\r
249         } else {\r
250                 memset(array, 0, size);\r
251                 struct s *p = &array[0];\r
252                 for (i = 0; i < count - 1; i++) {\r
253                         p->ptr = (struct s*)1; /* Mark as occupied to avoid self-loop */\r
254                         for (j = rand() % count;\r
255                              array[j].ptr != NULL;\r
256                              j = (j >= count) ? 0 : j+1);\r
257                         p = p->ptr = &array[j];\r
258                 }\r
259                 p->ptr = &array[0];\r
260         }\r
261 }\r
262 \r
263 static void do_read(struct s *array, unsigned reads)\r
264 {\r
265         unsigned i = reads / 32;\r
266         volatile struct s *p = &array[0];\r
267         while (--i) {\r
268                 p = p->ptr;     /* 0 */\r
269                 p = p->ptr;     /* 1 */\r
270                 p = p->ptr;     /* 2 */\r
271                 p = p->ptr;     /* 3 */\r
272                 p = p->ptr;     /* 4 */\r
273                 p = p->ptr;     /* 5 */\r
274                 p = p->ptr;     /* 6 */\r
275                 p = p->ptr;     /* 7 */\r
276                 p = p->ptr;     /* 8 */\r
277                 p = p->ptr;     /* 9 */\r
278                 p = p->ptr;     /* 10 */\r
279                 p = p->ptr;     /* 11 */\r
280                 p = p->ptr;     /* 12 */\r
281                 p = p->ptr;     /* 13 */\r
282                 p = p->ptr;     /* 14 */\r
283                 p = p->ptr;     /* 15 */\r
284                 p = p->ptr;     /* 16 */\r
285                 p = p->ptr;     /* 17 */\r
286                 p = p->ptr;     /* 18 */\r
287                 p = p->ptr;     /* 19 */\r
288                 p = p->ptr;     /* 20 */\r
289                 p = p->ptr;     /* 21 */\r
290                 p = p->ptr;     /* 22 */\r
291                 p = p->ptr;     /* 23 */\r
292                 p = p->ptr;     /* 24 */\r
293                 p = p->ptr;     /* 25 */\r
294                 p = p->ptr;     /* 26 */\r
295                 p = p->ptr;     /* 27 */\r
296                 p = p->ptr;     /* 28 */\r
297                 p = p->ptr;     /* 29 */\r
298                 p = p->ptr;     /* 30 */\r
299                 p = p->ptr;     /* 31 */\r
300         }\r
301 }\r
302 \r
303 static void do_write(struct s *array, unsigned accesses, unsigned ofs)\r
304 {\r
305         unsigned i = accesses / 32;\r
306         volatile struct s *p = &array[0];\r
307         while (--i) {\r
308                 p->dummy[ofs]++; p = p->ptr; /* 0 */\r
309                 p->dummy[ofs]++; p = p->ptr; /* 1 */\r
310                 p->dummy[ofs]++; p = p->ptr; /* 2 */\r
311                 p->dummy[ofs]++; p = p->ptr; /* 3 */\r
312                 p->dummy[ofs]++; p = p->ptr; /* 4 */\r
313                 p->dummy[ofs]++; p = p->ptr; /* 5 */\r
314                 p->dummy[ofs]++; p = p->ptr; /* 6 */\r
315                 p->dummy[ofs]++; p = p->ptr; /* 7 */\r
316                 p->dummy[ofs]++; p = p->ptr; /* 8 */\r
317                 p->dummy[ofs]++; p = p->ptr; /* 9 */\r
318                 p->dummy[ofs]++; p = p->ptr; /* 10 */\r
319                 p->dummy[ofs]++; p = p->ptr; /* 11 */\r
320                 p->dummy[ofs]++; p = p->ptr; /* 12 */\r
321                 p->dummy[ofs]++; p = p->ptr; /* 13 */\r
322                 p->dummy[ofs]++; p = p->ptr; /* 14 */\r
323                 p->dummy[ofs]++; p = p->ptr; /* 15 */\r
324                 p->dummy[ofs]++; p = p->ptr; /* 16 */\r
325                 p->dummy[ofs]++; p = p->ptr; /* 17 */\r
326                 p->dummy[ofs]++; p = p->ptr; /* 18 */\r
327                 p->dummy[ofs]++; p = p->ptr; /* 19 */\r
328                 p->dummy[ofs]++; p = p->ptr; /* 20 */\r
329                 p->dummy[ofs]++; p = p->ptr; /* 21 */\r
330                 p->dummy[ofs]++; p = p->ptr; /* 22 */\r
331                 p->dummy[ofs]++; p = p->ptr; /* 23 */\r
332                 p->dummy[ofs]++; p = p->ptr; /* 24 */\r
333                 p->dummy[ofs]++; p = p->ptr; /* 25 */\r
334                 p->dummy[ofs]++; p = p->ptr; /* 26 */\r
335                 p->dummy[ofs]++; p = p->ptr; /* 27 */\r
336                 p->dummy[ofs]++; p = p->ptr; /* 28 */\r
337                 p->dummy[ofs]++; p = p->ptr; /* 29 */\r
338                 p->dummy[ofs]++; p = p->ptr; /* 30 */\r
339                 p->dummy[ofs]++; p = p->ptr; /* 31 */\r
340         }\r
341 }\r
342 \r
343 struct benchmark_thread {\r
344         pthread_t id;\r
345         unsigned cpu;\r
346         uint64_t result_integral;\r
347         uint64_t result_fractional;\r
348         struct cfg *cfg;\r
349 };\r
350 \r
351 pthread_barrier_t barrier;\r
352 #ifdef JAILHOUSE\r
353 struct s array[MAX_CPUS][64*0x100000/sizeof(struct s)] __attribute__ ((aligned (2*1024*1024))) __attribute__ ((section (".bench-array")));\r
354 #else\r
355 struct s array[MAX_CPUS][64*0x100000/sizeof(struct s)] __attribute__ ((aligned (2*1024*1024)));\r
356 #endif\r
357 bool print = true;\r
358 \r
359 static void *benchmark_thread(void *arg)\r
360 {\r
361         struct benchmark_thread *me = arg;\r
362         cpu_set_t set;\r
363 \r
364         CPU_ZERO(&set);\r
365         CPU_SET(me->cpu, &set);\r
366 \r
367         if (pthread_setaffinity_np(me->id, sizeof(set), &set) != 0) {\r
368                 perror("pthread_setaffinity_np");\r
369                 exit(1);\r
370         }\r
371 \r
372         prepare(array[me->cpu], me->cfg->size, me->cfg->sequential);\r
373 \r
374         pthread_barrier_wait(&barrier);\r
375 \r
376         if (print)\r
377                 printf("CPU %d starts measurement\n", me->cpu);\r
378 \r
379         uint64_t tic, tac;\r
380         tic = get_time(me->cfg);\r
381         if (me->cfg->write == false)\r
382                 do_read(array[me->cpu], me->cfg->read_count);\r
383         else\r
384                 do_write(array[me->cpu], me->cfg->read_count, me->cfg->ofs);\r
385 \r
386         tac = get_time(me->cfg);\r
387         me->result_integral = (tac - tic) / me->cfg->read_count;\r
388         me->result_fractional = (((tac - tic) * 1000) / me->cfg->read_count) % 1000;\r
389         return NULL;\r
390 }\r
391 \r
392 static void run_benchmark(struct cfg *cfg)\r
393 {\r
394         struct benchmark_thread thread[MAX_CPUS];\r
395         unsigned i;\r
396         cpu_set_t set = cfg->cpu_set;\r
397         pthread_barrier_init(&barrier, NULL, cfg->num_threads);\r
398         for (i = 0; i < cfg->num_threads; i++) {\r
399                 thread[i].cfg = cfg;\r
400                 if (CPU_COUNT(&set) == 0) {\r
401                         thread[i].cpu = i;\r
402                 } else {\r
403                         int j;\r
404                         for (j = 0; j < MAX_CPUS; j++) {\r
405                                 if (CPU_ISSET(j, &set)) {\r
406                                         thread[i].cpu = j;\r
407                                         CPU_CLR(j, &set);\r
408                                         break;\r
409                                 }\r
410                         }\r
411                 }\r
412                 if (print)\r
413                         printf( "Running thread %d on CPU %d\n", i, thread[i].cpu);\r
414                 pthread_create(&thread[i].id, NULL, benchmark_thread, &thread[i]);\r
415         }\r
416 \r
417         for (i = 0; i < cfg->num_threads; i++) {\r
418                 pthread_join(thread[i].id, NULL);\r
419         }\r
420         pthread_barrier_destroy(&barrier);\r
421 \r
422         printf("%d", cfg->size);\r
423         for (i = 0; i < cfg->num_threads; i++) {\r
424                 //NOTE: Jailhouse is not able to print doubles.\r
425                 //printf("\t%#.3g", thread[i].result);\r
426                 printf("\t%lu.%03u", thread[i].result_integral, thread[i].result_fractional);\r
427         }\r
428         printf("\n");\r
429         fflush(stdout);\r
430         print = false;\r
431 }\r
432 \r
433 #ifdef JAILHOUSE\r
434 const void * image_end;\r
435 \r
436 void inmate_main(void)\r
437 #else //Linux\r
438 int main(int argc, char *argv[])\r
439 #endif\r
440 {\r
441         struct cfg cfg = {\r
442                 .sequential = true,\r
443                 .num_threads = 1,\r
444                 .size = 0,\r
445                 .read_count = 0x2000000,\r
446                 .write = false,\r
447                 .ofs = 0,\r
448                 .use_cycles = false, // i.e. use nanoseconds /\r
449                 .repeats = 1\r
450         };\r
451 #ifdef JAILHOUSE\r
452         //initialize UART\r
453         unsigned long tsc_freq;\r
454         unsigned int n;\r
455         printk_uart_base = UART_BASE;\r
456         do {\r
457                 for (n = 0; n < UART_IDLE_LOOPS; n++)\r
458                         if (!(inb(UART_BASE + UART_LSR) & UART_LSR_THRE))\r
459                                 break;\r
460         } while (n < UART_IDLE_LOOPS);\r
461 \r
462         printk("cmdline opts: '%s'\n", cmdline);\r
463         comm_region->pm_timer_address = 0x408;\r
464 \r
465         //parse cmdline\r
466         cfg.read_count = cmdline_parse_int("-c", cfg.read_count);\r
467         cfg.repeats = cmdline_parse_int("-R", cfg.repeats);\r
468         cfg.ofs = cmdline_parse_int("-o", cfg.ofs);\r
469         cfg.sequential = !cmdline_parse_bool("-r");\r
470         cfg.size = cmdline_parse_int("-s", cfg.size);\r
471         if (cmdline_parse_bool("-t")) {\r
472                 printk("Threads are not supported. '-t' was ignored.\n");\r
473         }\r
474         if (cmdline_parse_bool("-C")) {\r
475                 printk("CPU selection is not supported. '-C' was ignored.\n");\r
476         }\r
477         cfg.write = cmdline_parse_bool("-w");\r
478         cfg.use_cycles = cmdline_parse_bool("-y");\r
479         //initialize timing\r
480         tsc_freq = tsc_init();\r
481         printk("Calibrated TSC frequency: %lu.%03u kHz\n", tsc_freq / 1000,\r
482                tsc_freq % 1000);\r
483 \r
484         u8 * start_memreg = (u8 *) array;\r
485         u64 end_memreg_addr = (u64) &image_end;\r
486         while ( ((u64) start_memreg )< end_memreg_addr) {\r
487                 //printk("%p\n",start_memreg);\r
488                 map_range(start_memreg, HUGE_PAGE_SIZE, MAP_CACHED);\r
489                 start_memreg += HUGE_PAGE_SIZE;\r
490 \r
491         }\r
492 \r
493 #else //Linux param's parsing\r
494         CPU_ZERO(&cfg.cpu_set);\r
495 \r
496         int opt;\r
497         while ((opt = getopt(argc, argv, "c:C:o:rs:t:wyR:")) != -1) {\r
498                 switch (opt) {\r
499                 case 'c':\r
500                         cfg.read_count = atol(optarg);\r
501                         break;\r
502                 case 'o':\r
503                         cfg.ofs = atol(optarg);\r
504                         break;\r
505                 case 'r':       // random //\r
506                         cfg.sequential = false;\r
507                         break;\r
508                 case 's':\r
509                         cfg.size = atol(optarg);\r
510                         assert(cfg.size <= sizeof(array[0]));\r
511                         break;\r
512                 case 't':\r
513                         cfg.num_threads = atol(optarg);\r
514                         break;\r
515                 case 'C':\r
516                         CPU_SET(atol(optarg), &cfg.cpu_set);\r
517                         break;\r
518                 case 'w':\r
519                         cfg.write = true;\r
520                         break;\r
521                 case 'y':\r
522                         cfg.use_cycles = true;\r
523                         break;\r
524                 case 'R':\r
525                         cfg.repeats = atol(optarg);\r
526                         break;\r
527                 default: // '?' //\r
528                         fprintf(stderr, "Usage: %s ... TODO\n", argv[0]);\r
529                         exit(1);\r
530                 }\r
531         }\r
532 #endif\r
533 \r
534         srand(time(NULL));\r
535 \r
536         if (cfg.write) {\r
537                 struct s s;\r
538                 assert(cfg.ofs < ARRAY_SIZE(s.dummy));\r
539         }\r
540 \r
541         if (cfg.use_cycles)\r
542                 ccntr_init();\r
543 \r
544         for (;cfg.repeats > 0; cfg.repeats--) {\r
545                 if (cfg.size != 0) {\r
546                         run_benchmark(&cfg);\r
547                 } else {\r
548                          unsigned order, size, step;\r
549                          for (order = 10; order <= 24; order++) {\r
550                                 for (step = 0; step < 2; step++) {\r
551                                         size = 1 << order;\r
552                                         if (step == 1)\r
553                                                 size += size / 2;\r
554 \r
555                                         cfg.size = size;\r
556                                         run_benchmark(&cfg);\r
557                                 }\r
558                         }\r
559                         cfg.size = 0;\r
560                 }\r
561         }\r
562 \r
563 \r
564 #ifndef JAILHOUSE\r
565         return 0;\r
566 #else\r
567         comm_region->cell_state = JAILHOUSE_CELL_SHUT_DOWN;\r
568         printk("done!\n");\r
569 #endif\r
570 }\r