1 /*
2  *  Copyright (C) 2013, Northwestern University and Argonne National Laboratory
3  *  See COPYRIGHT notice in top-level directory.
4  */
5 /* $Id: header_consistency.c 2744 2016-12-28 16:25:22Z wkliao $ */
6 
7 /* This program tests if PnetCDF can detect file header inconsistency and
8  * overwrite the inconsistent header with root's.
9  * This program is designed to run on more than 2 MPI processes.
10  */
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <libgen.h> /* basename() */
16 #include <mpi.h>
17 #include <pnetcdf.h>
18 #include <testutils.h>
19 
20 #define ERR_EXP(e, exp) {if (e != exp) { printf("Error (line %d): expecting error code %s but got %s\n", __LINE__, nc_err_code_name(exp), nc_err_code_name(e)); nerrs++; }}
21 #define ERR_EXP2(e, exp1, exp2) {if (e != exp1 && e != exp2 && e != NC_EFILE) { printf("Error (line %d): expecting error code %s or %s but got %s\n", __LINE__, nc_err_code_name(exp1), nc_err_code_name(exp2), nc_err_code_name(e)); nerrs++; }}
22 
23 #define CHECK_ERR(expect) { \
24     if (safe_mode) { \
25         if (err != NC_EMULTIDEFINE && err != expect) { \
26             printf("Error (line %d): expecting error code NC_EMULTIDEFINE or %s but got %s\n", __LINE__, nc_err_code_name(expect), nc_err_code_name(err)); \
27             nerrs++; \
28         } \
29     } \
30     else if (rank > 0) { \
31         if (err != expect) { \
32             printf("Error (line %d): expecting error code %s but got %s\n", __LINE__, nc_err_code_name(expect), nc_err_code_name(err)); \
33             nerrs++; \
34         } \
35     } \
36 }
37 
38 #define ERR {if(err!=NC_NOERR) {printf("Error(%d) at line %d: %s\n",err,__LINE__,ncmpi_strerror(err)); nerrs++; }}
39 
40 /*----< test_open_mode() >----------------------------------------------------*/
41 static
test_open_mode(char * filename,int safe_mode)42 int test_open_mode(char *filename, int safe_mode)
43 {
44     int err, rank, ncid, cmode, omode, nerrs=0;
45     MPI_Info info=MPI_INFO_NULL;
46     MPI_Comm comm=MPI_COMM_WORLD;
47 
48     MPI_Comm_rank(comm, &rank);
49 
50     /* Test inconsistent cmode -----------------------------------------------*/
51     cmode = NC_CLOBBER|NC_64BIT_OFFSET;
52     if (rank == 0) cmode = NC_CLOBBER;
53     err = ncmpi_create(comm, filename, cmode, info, &ncid);
54     if (safe_mode)
55         ERR_EXP(err, NC_EMULTIDEFINE_CMODE)
56     else {
57         if (rank > 0) ERR_EXP(err, NC_EMULTIDEFINE_CMODE)
58         err = ncmpi_close(ncid); ERR
59     }
60 
61     /* Test inconsistent omode -----------------------------------------------*/
62     omode = NC_WRITE;
63     if (rank == 0) omode = NC_NOWRITE;
64     err = ncmpi_open(comm, filename, omode, info, &ncid);
65     if (safe_mode)
66         ERR_EXP(err, NC_EMULTIDEFINE_OMODE)
67     else {
68         if (rank > 0) ERR_EXP(err, NC_EMULTIDEFINE_OMODE)
69         err = ncmpi_close(ncid); ERR
70     }
71 
72     return nerrs;
73 }
74 
75 /*----< test_dim() >----------------------------------------------------------*/
76 static
test_dim(char * filename,int safe_mode)77 int test_dim(char *filename, int safe_mode)
78 {
79     int err, rank, ncid, cmode, dimid1, dimid2, dimid3, nerrs=0;
80     MPI_Info info=MPI_INFO_NULL;
81     MPI_Comm comm=MPI_COMM_WORLD;
82 
83     MPI_Comm_rank(comm, &rank);
84     cmode = NC_CLOBBER|NC_64BIT_OFFSET;
85 
86     /* Test inconsistency on dimension names ---------------------------------*/
87     err = ncmpi_create(comm, filename, cmode, info, &ncid); ERR
88     if (rank == 0)
89         err = ncmpi_def_dim(ncid, "y", 100, &dimid1);
90     else
91         err = ncmpi_def_dim(ncid, "xx", 100, &dimid1);
92     if (safe_mode)
93         CHECK_ERR(NC_EMULTIDEFINE_DIM_NAME)
94     else
95         ERR
96     err = ncmpi_enddef(ncid); ERR
97 
98     if (safe_mode) {
99         /* no processes should be able to see dim "y" */
100         err = ncmpi_inq_dimid(ncid, "y", &dimid2);
101         CHECK_ERR(NC_EBADDIM)
102 
103         /* no process should be able to see dim "x" */
104         err = ncmpi_inq_dimid(ncid, "xx", &dimid3);
105         CHECK_ERR(NC_EBADDIM)
106     }
107 
108     err = ncmpi_close(ncid); ERR
109 
110     /* Test inconsistency on dimension size ----------------------------------*/
111     err = ncmpi_create(comm, filename, cmode, info, &ncid); ERR
112     if (rank == 0)
113         err = ncmpi_def_dim(ncid, "x", 99, &dimid1);
114     else
115         err = ncmpi_def_dim(ncid, "x", 100, &dimid1);
116     if (safe_mode)
117         CHECK_ERR(NC_EMULTIDEFINE_DIM_SIZE)
118     else
119         ERR
120 
121     err = ncmpi_close(ncid); ERR
122     return nerrs;
123 }
124 
125 /*----< test_attr() >---------------------------------------------------------*/
126 static
test_attr(char * filename,int safe_mode)127 int test_attr(char *filename, int safe_mode)
128 {
129     int err, rank, ncid, cmode, nerrs=0;
130     char  gattr[128];
131     int   int_attr;
132     float flt_attr;
133     MPI_Info info=MPI_INFO_NULL;
134     MPI_Comm comm=MPI_COMM_WORLD;
135 
136     MPI_Comm_rank(comm, &rank);
137     cmode = NC_CLOBBER|NC_64BIT_OFFSET;
138 
139     /* Test inconsistent global attribute name -------------------------------*/
140     err = ncmpi_create(comm, filename, cmode, info, &ncid); ERR
141     int_attr = 1;
142     sprintf(gattr, "gattr_name.%d",rank);
143     err = ncmpi_put_att_int(ncid, NC_GLOBAL, gattr, NC_INT, 1, &int_attr);
144     if (safe_mode)
145         CHECK_ERR(NC_EMULTIDEFINE_ATTR_NAME)
146     else
147         ERR
148     err = ncmpi_close(ncid); ERR
149 
150     /* Test inconsistent global attribute type -------------------------------*/
151     err = ncmpi_create(comm, filename, cmode, info, &ncid); ERR
152     if (rank == 0)
153         err = ncmpi_put_att_int(ncid, NC_GLOBAL, "gatt", NC_INT, 1, &int_attr);
154     else
155         err = ncmpi_put_att_float(ncid, NC_GLOBAL, "gatt", NC_FLOAT, 1, &flt_attr);
156     if (safe_mode)
157         CHECK_ERR(NC_EMULTIDEFINE_ATTR_TYPE)
158     else
159         ERR
160     err = ncmpi_close(ncid); ERR
161 
162     /* Test inconsistent global attribute length -----------------------------*/
163     err = ncmpi_create(comm, filename, cmode, info, &ncid); ERR
164     int intv[2]={1,2};
165     if (rank == 0)
166         err = ncmpi_put_att_int(ncid, NC_GLOBAL, "gatt", NC_INT, 2, intv);
167     else
168         err = ncmpi_put_att_int(ncid, NC_GLOBAL, "gatt", NC_INT, 1, intv);
169     if (safe_mode)
170         CHECK_ERR(NC_EMULTIDEFINE_ATTR_LEN)
171     else
172         ERR
173     err = ncmpi_close(ncid); ERR
174 
175     /* Test inconsistent global attribute length -----------------------------*/
176     err = ncmpi_create(comm, filename, cmode, info, &ncid); ERR
177     if (rank == 0) intv[1]=3;
178     err = ncmpi_put_att_int(ncid, NC_GLOBAL, "gatt", NC_INT, 2, intv);
179     if (safe_mode)
180         CHECK_ERR(NC_EMULTIDEFINE_ATTR_VAL)
181     else
182         ERR
183     err = ncmpi_close(ncid); ERR
184 
185     return nerrs;
186 }
187 
188 /*----< test_var() >----------------------------------------------------------*/
189 static
test_var(char * filename,int safe_mode)190 int test_var(char *filename, int safe_mode)
191 {
192     int err, rank, ncid, cmode, nerrs=0;
193     int dimid[3], varid1, int_attr;
194     float flt_attr;
195     char name[128], var_attr[128];
196     MPI_Info info=MPI_INFO_NULL;
197     MPI_Comm comm=MPI_COMM_WORLD;
198 
199     MPI_Comm_rank(comm, &rank);
200     cmode = NC_CLOBBER|NC_64BIT_OFFSET;
201 
202     /* Test inconsistent global attribute name -------------------------------*/
203     err = ncmpi_create(comm, filename, cmode, info, &ncid); ERR
204     err = ncmpi_def_dim(ncid, "dim1", NC_UNLIMITED, &dimid[0]); ERR
205     err = ncmpi_def_var(ncid, "var1", NC_INT, 1, dimid, &varid1); ERR
206     int_attr = 1;
207     sprintf(var_attr, "var_attr_name.%d",rank);
208     err = ncmpi_put_att_int(ncid, varid1, var_attr, NC_INT, 1, &int_attr);
209     if (safe_mode)
210         CHECK_ERR(NC_EMULTIDEFINE_ATTR_NAME)
211     else
212         ERR
213     err = ncmpi_close(ncid); ERR
214 
215     /* Test inconsistent global attribute type -------------------------------*/
216     err = ncmpi_create(comm, filename, cmode, info, &ncid); ERR
217     err = ncmpi_def_dim(ncid, "dim1", NC_UNLIMITED, &dimid[0]); ERR
218     err = ncmpi_def_var(ncid, "var1", NC_INT, 1, dimid, &varid1); ERR
219     if (rank == 0)
220         err = ncmpi_put_att_int(ncid, varid1, "var_att", NC_INT, 1, &int_attr);
221     else
222         err = ncmpi_put_att_float(ncid, varid1, "var_att", NC_FLOAT, 1, &flt_attr);
223     if (safe_mode)
224         CHECK_ERR(NC_EMULTIDEFINE_ATTR_TYPE)
225     else
226         ERR
227     err = ncmpi_close(ncid); ERR
228 
229     /* Test inconsistent global attribute length -----------------------------*/
230     err = ncmpi_create(comm, filename, cmode, info, &ncid); ERR
231     err = ncmpi_def_dim(ncid, "dim1", NC_UNLIMITED, &dimid[0]); ERR
232     err = ncmpi_def_var(ncid, "var1", NC_INT, 1, dimid, &varid1); ERR
233     int intv[2]={1,2};
234     if (rank == 0)
235         err = ncmpi_put_att_int(ncid, varid1, "var_att", NC_INT, 2, intv);
236     else
237         err = ncmpi_put_att_int(ncid, varid1, "var_att", NC_INT, 1, intv);
238     if (safe_mode)
239         CHECK_ERR(NC_EMULTIDEFINE_ATTR_LEN)
240     else
241         ERR
242     err = ncmpi_close(ncid); ERR
243 
244     /* Test inconsistent global attribute length -----------------------------*/
245     err = ncmpi_create(comm, filename, cmode, info, &ncid); ERR
246     err = ncmpi_def_dim(ncid, "dim1", NC_UNLIMITED, &dimid[0]); ERR
247     err = ncmpi_def_var(ncid, "var1", NC_INT, 1, dimid, &varid1); ERR
248     if (rank == 0) intv[1]=3;
249     err = ncmpi_put_att_int(ncid, varid1, "var_att", NC_INT, 2, intv);
250     if (safe_mode)
251         CHECK_ERR(NC_EMULTIDEFINE_ATTR_VAL)
252     else
253         ERR
254     err = ncmpi_close(ncid); ERR
255 
256     /* Test inconsistent variable name ---------------------------------------*/
257     err = ncmpi_create(comm, filename, cmode, info, &ncid); ERR
258     err = ncmpi_def_dim(ncid, "dim1", NC_UNLIMITED, &dimid[0]); ERR
259     sprintf(name, "var.%d",rank);
260     err = ncmpi_def_var(ncid, name, NC_INT, 1, dimid, &varid1);
261     if (safe_mode)
262         CHECK_ERR(NC_EMULTIDEFINE_VAR_NAME)
263     else
264         ERR
265     err = ncmpi_close(ncid); ERR
266 
267     /* Test inconsistent variable ndims --------------------------------------*/
268     err = ncmpi_create(comm, filename, cmode, info, &ncid); ERR
269     err = ncmpi_def_dim(ncid, "dim0", 3, &dimid[0]); ERR
270     err = ncmpi_def_dim(ncid, "dim1", 2, &dimid[1]); ERR
271     if (rank == 0)
272         err = ncmpi_def_var(ncid, "var", NC_FLOAT, 2, dimid, &varid1);
273     else
274         err = ncmpi_def_var(ncid, "var", NC_FLOAT, 1, dimid, &varid1);
275     if (safe_mode)
276         CHECK_ERR(NC_EMULTIDEFINE_VAR_NDIMS)
277     else
278         ERR
279     err = ncmpi_close(ncid); ERR
280 
281     /* Test inconsistent variable type ---------------------------------------*/
282     err = ncmpi_create(comm, filename, cmode, info, &ncid); ERR
283     err = ncmpi_def_dim(ncid, "dim1", NC_UNLIMITED, &dimid[0]); ERR
284     if (rank == 0)
285         err = ncmpi_def_var(ncid, "var", NC_INT, 1, dimid, &varid1);
286     else
287         err = ncmpi_def_var(ncid, "var", NC_FLOAT, 1, dimid, &varid1);
288     if (safe_mode)
289         CHECK_ERR(NC_EMULTIDEFINE_VAR_TYPE)
290     else
291         ERR
292     err = ncmpi_close(ncid); ERR
293 
294     /* Test inconsistent variable length -------------------------------------*/
295     err = ncmpi_create(comm, filename, cmode, info, &ncid); ERR
296     err = ncmpi_def_dim(ncid, "dim0", 5, &dimid[0]); ERR
297     err = ncmpi_def_dim(ncid, "dim1", 4, &dimid[1]); ERR
298     err = ncmpi_def_dim(ncid, "dim2", 3, &dimid[2]); ERR
299     if (rank == 0)
300         err = ncmpi_def_var(ncid, "var", NC_FLOAT, 2, dimid, &varid1);
301     else
302         err = ncmpi_def_var(ncid, "var", NC_FLOAT, 2, dimid+1, &varid1);
303     if (safe_mode)
304         CHECK_ERR(NC_EMULTIDEFINE_VAR_DIMIDS)
305     else
306         ERR
307     err = ncmpi_close(ncid); ERR
308 
309     /* Test inconsistent variable dimension IDs ------------------------------*/
310     err = ncmpi_create(comm, filename, cmode, info, &ncid); ERR
311     err = ncmpi_def_dim(ncid, "Z", 3, &dimid[0]); ERR
312     err = ncmpi_def_dim(ncid, "Y", 3, &dimid[1]); ERR
313     err = ncmpi_def_dim(ncid, "X", 3, &dimid[2]); ERR
314     if (rank == 0)
315         err = ncmpi_def_var(ncid, "var", NC_FLOAT, 2, dimid+1, &varid1);
316     else
317         err = ncmpi_def_var(ncid, "var", NC_FLOAT, 2, dimid, &varid1);
318     if (safe_mode)
319         CHECK_ERR(NC_EMULTIDEFINE_VAR_DIMIDS)
320     else
321         ERR
322 
323     err = ncmpi_close(ncid); ERR
324 
325     return nerrs;
326 }
327 
328 /*----< main() >--------------------------------------------------------------*/
main(int argc,char ** argv)329 int main(int argc, char **argv)
330 {
331     char *filename="testfile.nc", *mode[2] = {"0", "1"};
332     int i, rank, nprocs, verbose, nerrs=0;
333 
334     MPI_Init(&argc, &argv);
335     MPI_Comm_rank(MPI_COMM_WORLD, &rank);
336     MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
337 
338     if (nprocs < 2) {
339         if (!rank) printf("This program is for running 2 or more processes. Exiting ...\n");
340         MPI_Finalize();
341         return 0;
342     }
343 
344     if (argc > 2) {
345         if (!rank) printf("Usage: %s [filename]\n",argv[0]);
346         MPI_Finalize();
347         return 0;
348     }
349     if (argc == 2) filename = argv[1];
350 
351     if (rank == 0) {
352         char *cmd_str = (char*)malloc(strlen(argv[0]) + 256);
353         sprintf(cmd_str, "*** TESTING C   %s for header consistency", basename(argv[0]));
354         printf("%-66s ------ ", cmd_str);
355         free(cmd_str);
356     }
357 
358     verbose = 1;
359     for (i=verbose; i>=0; i--) {
360         /* test with safe mode off and on :
361          * Note even if --enable-debug is set at configure time, safe mode
362          * can still be disabled by setting the environment variable
363          * PNETCDF_SAFE_MODE to 0.
364          */
365         setenv("PNETCDF_SAFE_MODE", mode[i], 1);
366         nerrs += test_open_mode(filename, i);
367 
368         nerrs += test_dim(filename, i);
369 
370         nerrs += test_attr(filename, i);
371 
372         nerrs += test_var(filename, i);
373     }
374 
375     MPI_Offset malloc_size, sum_size;
376     int err = ncmpi_inq_malloc_size(&malloc_size);
377     if (err == NC_NOERR) {
378         MPI_Reduce(&malloc_size, &sum_size, 1, MPI_OFFSET, MPI_SUM, 0, MPI_COMM_WORLD);
379         if (rank == 0 && sum_size > 0)
380             printf("heap memory allocated by PnetCDF internally has %lld bytes yet to be freed\n",
381                    sum_size);
382     }
383 
384     MPI_Allreduce(MPI_IN_PLACE, &nerrs, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
385     if (rank == 0) {
386         if (nerrs) printf(FAIL_STR,nerrs);
387         else       printf(PASS_STR);
388     }
389 
390     MPI_Finalize();
391     return 0;
392 }
393 
394