diff --git a/example_problems/hello/submissions/wrong_answer/test-cpu-pinning.c b/example_problems/hello/submissions/wrong_answer/test-cpu-pinning.c new file mode 100644 index 0000000000..daa4d08906 --- /dev/null +++ b/example_problems/hello/submissions/wrong_answer/test-cpu-pinning.c @@ -0,0 +1,72 @@ +/* + * This code forks 10 children and checks that those run on the same + * CPU core as the parent. Since we're normally doing CPU pinning, + * this should be true and the program generates a WRONG-ANWSER. + * If there's a mismatch the program will trigger a RUNTIME-ERROR. + * + * @EXPECTED_RESULTS@: WRONG-ANSWER + */ + +#include +#include +#include +#include +#include +#include + +extern int errno; + +int main() +{ + unsigned int parent_cpu, cpu; + getcpu(&parent_cpu, NULL); + printf("Parent on CPU %d\n", parent_cpu); + fflush(NULL); + + for(int i=0; i<10; i++) { + if ( fork()==0 ) { + // We're in the child + getcpu(&cpu, NULL); + printf("Child %d on CPU %d\n", i, cpu); + fflush(NULL); + if ( cpu!=parent_cpu ) { + printf("CPU MISMATCH!\n"); + fflush(NULL); + return 1; + } + return 0; + } + } + + // Wait for all our children + do { + pid_t pid; + int status; + pid = wait(&status); + if ( pid==-1 ) { + if ( errno==ECHILD ) { + printf("Done waiting for all children\n"); + } else { + printf("Error in wait: %s\n", strerror(errno)); + } + fflush(NULL); + break; + } + if ( pid>0 ) { + if ( !WIFEXITED(status) ) { + printf("Child pid %d terminated abnormally\n", (int)pid); + fflush(NULL); + return 1; + } + if ( WEXITSTATUS(status)!=0 ) { + printf("Child pid %d terminated with exit status %d\n", + (int)pid, (int)WEXITSTATUS(status)); + return 1; + } + printf("Child pid %d terminated\n", (int)pid); + fflush(NULL); + } + } while ( 1 ); + + return 0; +} diff --git a/judge/runguard.cc b/judge/runguard.cc index ca92a7785b..6158d12dc3 100644 --- a/judge/runguard.cc +++ b/judge/runguard.cc @@ -63,6 +63,8 @@ #include #include #include +#include +#include #include #include @@ -465,6 +467,50 @@ void output_exit_time(int exitcode, double cpudiff) write_meta("time-result","%s",output_timelimit_str[timelimit_reached]); } +std::set parse_cpuset(std::string cpus) +{ + std::stringstream ss(cpus); + std::set result; + + std::string token; + while ( getline(ss, token, ',') ) { + size_t split = token.find('-'); + if ( split!=std::string::npos ) { + std::string token1 = token.substr(0, split); + std::string token2 = token.substr(split+1); + size_t len; + unsigned cpu1 = std::stoul(token1, &len); + if ( len read_cpuset(const char *path) +{ + FILE *file = fopen(path, "r"); + if (file == nullptr) error(errno, "opening file `%s'", path); + + char cpuset[1024]; + if (fgets(cpuset, 1024, file) == nullptr) error(errno, "reading from file `%s'", path); + + size_t len = strlen(cpuset); + if (len > 0 && cpuset[len-1] == '\n') cpuset[len-1] = 0; + + if (fclose(file) != 0) error(errno, "closing file `%s'", path); + + return parse_cpuset(cpuset); +} + void check_remaining_procs() { char path[1024]; @@ -494,9 +540,8 @@ void output_cgroup_stats_v1(double *cputime) int ret; if ((ret = cgroup_get_cgroup(cg)) != 0) error(ret,"get cgroup information"); - int64_t max_usage; - struct cgroup_controller *cg_controller; - cg_controller = cgroup_get_controller(cg, "memory"); + int64_t max_usage = 0; + struct cgroup_controller *cg_controller = cgroup_get_controller(cg, "memory"); ret = cgroup_get_value_int64(cg_controller, "memory.memsw.max_usage_in_bytes", &max_usage); if ( ret!=0 ) error(ret,"get cgroup value memory.memsw.max_usage_in_bytes"); @@ -516,7 +561,7 @@ void output_cgroup_stats_v1(double *cputime) void output_cgroup_stats_v2(double *cputime) { struct cgroup *cg; - if ( (cg = cgroup_new_cgroup(cgroupname))==NULL ) error(0,"cgroup_new_cgroup"); + if ( (cg = cgroup_new_cgroup(cgroupname))==nullptr ) error(0,"cgroup_new_cgroup"); int ret; if ((ret = cgroup_get_cgroup(cg)) != 0) error(ret,"get cgroup information"); @@ -540,7 +585,7 @@ void output_cgroup_stats_v2(double *cputime) while (ret == 0) { verbose("cpu.stat: %s = %s", stat.name, stat.value); if (strcmp(stat.name, "usage_usec") == 0) { - long long usec = strtoll(stat.value, NULL, 10); + long long usec = strtoll(stat.value, nullptr, 10); *cputime = usec / 1e6; } ret = cgroup_read_stats_next(&handle, &stat); @@ -549,7 +594,6 @@ void output_cgroup_stats_v2(double *cputime) cgroup_read_stats_end(&handle); cgroup_free(&cg); - } /* Temporary shorthand define for error handling. */ @@ -901,7 +945,7 @@ void setrestrictions() /* Put the child process in the cgroup */ if (is_cgroup_v2) { - const char *controllers[] = { "memory", NULL }; + const char *controllers[] = { "memory", nullptr }; if (cgroup_change_cgroup_path(cgroupname, getpid(), controllers) != 0) { error(0, "Failed to move the process to the cgroup"); } @@ -950,7 +994,7 @@ void setrestrictions() /* Set group-id (must be root for this, so before setting user). */ if ( use_group ) { if ( setgid(rungid) ) error(errno,"cannot set group ID to `%d'",rungid); - if ( setgroups(0, NULL) ) error(errno,"cannot clear auxiliary groups"); + if ( setgroups(0, nullptr) ) error(errno,"cannot clear auxiliary groups"); verbose("using group ID `%d'",rungid); } @@ -1116,7 +1160,7 @@ int main(int argc, char **argv) case 'u': /* user option: uid or string */ use_user = 1; runuser = strdup(optarg); - if ( runuser==NULL ) error(errno,"strdup() failed"); + if ( runuser==nullptr ) error(errno,"strdup() failed"); errno = 0; runuid = strtol(optarg,&ptr,10); if ( errno || *ptr!='\0' ) { @@ -1133,7 +1177,7 @@ int main(int argc, char **argv) case 'g': /* group option: gid or string */ use_group = 1; rungroup = strdup(optarg); - if ( rungroup==NULL ) error(errno,"strdup() failed"); + if ( rungroup==nullptr ) error(errno,"strdup() failed"); errno = 0; rungid = strtol(optarg,&ptr,10); if ( errno || *ptr!='\0' ) rungid = groupid(optarg); @@ -1298,14 +1342,12 @@ int main(int argc, char **argv) } if ( cpuset!=nullptr && strlen(cpuset)>0 ) { - int ret = strtol(cpuset, &ptr, 10); - /* check if input is only a single integer */ - if ( *ptr == '\0' ) { - /* check if we have enough cores available */ - int nprocs = get_nprocs_conf(); - if ( ret < 0 || ret >= nprocs ) { - error(0, "processor ID %d given as cpuset, but only %d cores configured", - ret, nprocs); + std::set cpus = parse_cpuset(cpuset); + std::set online_cpus = read_cpuset("/sys/devices/system/cpu/online"); + + for(unsigned cpu : cpus) { + if ( !online_cpus.count(cpu) ) { + error(0, "requested pinning on CPU %u which is not online", cpu); } } } @@ -1488,7 +1530,7 @@ int main(int argc, char **argv) } } - int r = pselect(nfds+1, &readfds, nullptr, NULL, NULL, &emptymask); + int r = pselect(nfds+1, &readfds, nullptr, nullptr, nullptr, &emptymask); if ( r==-1 && errno!=EINTR ) error(errno,"waiting for child data"); if (error_in_signalhandler) { error(errno, "error in signal handler, exiting");