blkparse: split off the timestamp correction code in to a separate function
[blktrace.git] / blktrace.c
index 63125ab4d08f4ba954e1ec63ba84dbede716df43..d0d271f9add5106b4481321058787f1ba1aa82a4 100644 (file)
@@ -112,6 +112,7 @@ struct devpath {
        struct cl_host *ch;
        u32 cl_id;
        time_t cl_connect_time;
+       int setup_done; /* ioctl BLKTRACESETUP done */
        struct io_info *ios;
 };
 
@@ -274,7 +275,9 @@ static char blktrace_version[] = "2.0.0";
 int data_is_native = -1;
 
 static int ndevs;
+static int max_cpus;
 static int ncpus;
+static cpu_set_t *online_cpus;
 static int pagesize;
 static int act_mask = ~0U;
 static int kill_running_trace;
@@ -621,13 +624,20 @@ static void dpp_free(struct devpath *dpp)
 
 static int lock_on_cpu(int cpu)
 {
-       cpu_set_t cpu_mask;
+       cpu_set_t * cpu_mask;
+       size_t size;
 
-       CPU_ZERO(&cpu_mask);
-       CPU_SET(cpu, &cpu_mask);
-       if (sched_setaffinity(0, sizeof(cpu_mask), &cpu_mask) < 0)
+       cpu_mask = CPU_ALLOC(max_cpus);
+       size = CPU_ALLOC_SIZE(max_cpus);
+
+       CPU_ZERO_S(size, cpu_mask);
+       CPU_SET_S(cpu, size, cpu_mask);
+       if (sched_setaffinity(0, size, cpu_mask) < 0) {
+               CPU_FREE(cpu_mask);             
                return errno;
+       }
 
+       CPU_FREE(cpu_mask);             
        return 0;
 }
 
@@ -876,7 +886,7 @@ static int net_send_header(int fd, int cpu, char *buts_name, int len)
        strncpy(hdr.buts_name, buts_name, sizeof(hdr.buts_name));
        hdr.buts_name[sizeof(hdr.buts_name) - 1] = '\0';
        hdr.cpu = cpu;
-       hdr.max_cpus = ncpus;
+       hdr.max_cpus = max_cpus;
        hdr.len = len;
        hdr.cl_id = getpid();
        hdr.buf_size = buf_size;
@@ -1020,9 +1030,12 @@ static int net_setup_client(void)
 static int open_client_connections(void)
 {
        int cpu;
+       size_t alloc_size = CPU_ALLOC_SIZE(max_cpus);
 
        cl_fds = calloc(ncpus, sizeof(*cl_fds));
-       for (cpu = 0; cpu < ncpus; cpu++) {
+       for (cpu = 0; cpu < max_cpus; cpu++) {
+               if (!CPU_ISSET_S(cpu, alloc_size, online_cpus))
+                       continue;
                cl_fds[cpu] = net_setup_client();
                if (cl_fds[cpu] < 0)
                        goto err;
@@ -1040,8 +1053,11 @@ static void close_client_connections(void)
 {
        if (cl_fds) {
                int cpu, *fdp;
+               size_t alloc_size = CPU_ALLOC_SIZE(max_cpus);
 
-               for (cpu = 0, fdp = cl_fds; cpu < ncpus; cpu++, fdp++) {
+               for (cpu = 0, fdp = cl_fds; cpu < max_cpus; cpu++, fdp++) {
+                       if (!CPU_ISSET_S(cpu, alloc_size, online_cpus))
+                               continue;
                        if (*fdp >= 0) {
                                net_send_drops(*fdp);
                                net_close_connection(fdp);
@@ -1051,9 +1067,10 @@ static void close_client_connections(void)
        }
 }
 
-static void setup_buts(void)
+static int setup_buts(void)
 {
        struct list_head *p;
+       int ret = 0;
 
        __list_for_each(p, &devpaths) {
                struct blk_user_trace_setup buts;
@@ -1065,16 +1082,21 @@ static void setup_buts(void)
                buts.act_mask = act_mask;
 
                if (ioctl(dpp->fd, BLKTRACESETUP, &buts) >= 0) {
-                       dpp->ncpus = ncpus;
+                       dpp->ncpus = max_cpus;
                        dpp->buts_name = strdup(buts.name);
+                       dpp->setup_done = 1;
                        if (dpp->stats)
                                free(dpp->stats);
                        dpp->stats = calloc(dpp->ncpus, sizeof(*dpp->stats));
                        memset(dpp->stats, 0, dpp->ncpus * sizeof(*dpp->stats));
-               } else
+               } else {
                        fprintf(stderr, "BLKTRACESETUP(2) %s failed: %d/%s\n",
                                dpp->path, errno, strerror(errno));
+                       ret++;
+               }
        }
+
+       return ret;
 }
 
 static void start_buts(void)
@@ -1149,7 +1171,7 @@ static void free_tracer_heads(struct devpath *dpp)
        int cpu;
        struct tracer_devpath_head *hd;
 
-       for (cpu = 0, hd = dpp->heads; cpu < ncpus; cpu++, hd++) {
+       for (cpu = 0, hd = dpp->heads; cpu < max_cpus; cpu++, hd++) {
                if (hd->prev)
                        free(hd->prev);
 
@@ -1171,8 +1193,8 @@ static int setup_tracer_devpaths(void)
                struct tracer_devpath_head *hd;
                struct devpath *dpp = list_entry(p, struct devpath, head);
 
-               dpp->heads = calloc(ncpus, sizeof(struct tracer_devpath_head));
-               for (cpu = 0, hd = dpp->heads; cpu < ncpus; cpu++, hd++) {
+               dpp->heads = calloc(max_cpus, sizeof(struct tracer_devpath_head));
+               for (cpu = 0, hd = dpp->heads; cpu < max_cpus; cpu++, hd++) {
                        INIT_LIST_HEAD(&hd->head);
                        pthread_mutex_init(&hd->mutex, NULL);
                        hd->prev = NULL;
@@ -1265,7 +1287,8 @@ static void rel_devpaths(void)
                struct devpath *dpp = list_entry(p, struct devpath, head);
 
                list_del(&dpp->head);
-               __stop_trace(dpp->fd);
+               if (dpp->setup_done)
+                       __stop_trace(dpp->fd);
                close(dpp->fd);
 
                if (dpp->heads)
@@ -1420,7 +1443,7 @@ static void __process_trace_bufs(void)
                struct devpath *dpp = list_entry(p, struct devpath, head);
                struct tracer_devpath_head *hd = dpp->heads;
 
-               for (cpu = 0; cpu < ncpus; cpu++, hd++) {
+               for (cpu = 0; cpu < max_cpus; cpu++, hd++) {
                        pthread_mutex_lock(&hd->mutex);
                        if (list_empty(&hd->head)) {
                                pthread_mutex_unlock(&hd->mutex);
@@ -1487,30 +1510,25 @@ static inline int net_sendfile_data(struct tracer *tp, struct io_info *iop)
        return net_sendfile(iop);
 }
 
-static int fill_ofname(struct io_info *iop, int cpu)
+static int fill_ofname(char *dst, int dstlen, char *subdir, char *buts_name,
+                      int cpu)
 {
        int len;
        struct stat sb;
-       char *dst = iop->ofn;
 
        if (output_dir)
-               len = snprintf(iop->ofn, sizeof(iop->ofn), "%s/", output_dir);
+               len = snprintf(dst, dstlen, "%s/", output_dir);
        else
-               len = snprintf(iop->ofn, sizeof(iop->ofn), "./");
-
-       if (net_mode == Net_server) {
-               struct cl_conn *nc = iop->nc;
+               len = snprintf(dst, dstlen, "./");
 
-               len += sprintf(dst + len, "%s-", nc->ch->hostname);
-               len += strftime(dst + len, 64, "%F-%T/",
-                               gmtime(&iop->dpp->cl_connect_time));
-       }
+       if (subdir)
+               len += snprintf(dst + len, dstlen - len, "%s", subdir);
 
-       if (stat(iop->ofn, &sb) < 0) {
+       if (stat(dst, &sb) < 0) {
                if (errno != ENOENT) {
                        fprintf(stderr,
                                "Destination dir %s stat failed: %d/%s\n",
-                               iop->ofn, errno, strerror(errno));
+                               dst, errno, strerror(errno));
                        return 1;
                }
                /*
@@ -1518,20 +1536,20 @@ static int fill_ofname(struct io_info *iop, int cpu)
                 * trying to create the directory at once.  It's harmless
                 * to let them try, so just detect the problem and move on.
                 */
-               if (mkdir(iop->ofn, 0755) < 0 && errno != EEXIST) {
+               if (mkdir(dst, 0755) < 0 && errno != EEXIST) {
                        fprintf(stderr,
                                "Destination dir %s can't be made: %d/%s\n",
-                               iop->ofn, errno, strerror(errno));
+                               dst, errno, strerror(errno));
                        return 1;
                }
        }
 
        if (output_name)
-               snprintf(iop->ofn + len, sizeof(iop->ofn), "%s.blktrace.%d",
+               snprintf(dst + len, dstlen - len, "%s.blktrace.%d",
                         output_name, cpu);
        else
-               snprintf(iop->ofn + len, sizeof(iop->ofn), "%s.blktrace.%d",
-                        iop->dpp->buts_name, cpu);
+               snprintf(dst + len, dstlen - len, "%s.blktrace.%d",
+                        buts_name, cpu);
 
        return 0;
 }
@@ -1552,8 +1570,23 @@ static int set_vbuf(struct io_info *iop, int mode, size_t size)
 
 static int iop_open(struct io_info *iop, int cpu)
 {
+       char hostdir[MAXPATHLEN + 64];
+
        iop->ofd = -1;
-       if (fill_ofname(iop, cpu))
+       if (net_mode == Net_server) {
+               struct cl_conn *nc = iop->nc;
+               int len;
+
+               len = snprintf(hostdir, sizeof(hostdir), "%s-",
+                              nc->ch->hostname);
+               len += strftime(hostdir + len, sizeof(hostdir) - len, "%F-%T/",
+                               gmtime(&iop->dpp->cl_connect_time));
+       } else {
+               hostdir[0] = 0;
+       }
+
+       if (fill_ofname(iop->ofn, sizeof(iop->ofn), hostdir,
+                       iop->dpp->buts_name, cpu))
                return 1;
 
        iop->ofp = my_fopen(iop->ofn, "w+");
@@ -1868,16 +1901,49 @@ static int start_tracer(int cpu)
        return 0;
 }
 
+static int create_output_files(int cpu)
+{
+       char fname[MAXPATHLEN + 64];
+       struct list_head *p;
+       FILE *f;
+
+       __list_for_each(p, &devpaths) {
+               struct devpath *dpp = list_entry(p, struct devpath, head);
+
+               if (fill_ofname(fname, sizeof(fname), NULL, dpp->buts_name,
+                               cpu))
+                       return 1;
+               f = my_fopen(fname, "w+");
+               if (!f)
+                       return 1;
+               fclose(f);
+       }
+       return 0;
+}
+
 static void start_tracers(void)
 {
-       int cpu;
+       int cpu, started = 0;
        struct list_head *p;
+       size_t alloc_size = CPU_ALLOC_SIZE(max_cpus);
 
-       for (cpu = 0; cpu < ncpus; cpu++)
+       for (cpu = 0; cpu < max_cpus; cpu++) {
+               if (!CPU_ISSET_S(cpu, alloc_size, online_cpus)) {
+                       /*
+                        * Create fake empty output files so that other tools
+                        * like blkparse don't have to bother with sparse CPU
+                        * number space.
+                        */
+                       if (create_output_files(cpu))
+                               break;
+                       continue;
+               }
                if (start_tracer(cpu))
                        break;
+               started++;
+       }
 
-       wait_tracers_ready(cpu);
+       wait_tracers_ready(started);
 
        __list_for_each(p, &tracers) {
                struct tracer *tp = list_entry(p, struct tracer, head);
@@ -1907,6 +1973,7 @@ static void stop_tracers(void)
                struct tracer *tp = list_entry(p, struct tracer, head);
                tp->is_done = 1;
        }
+       pthread_cond_broadcast(&mt_cond);
 }
 
 static void del_tracers(void)
@@ -2617,7 +2684,8 @@ static int run_tracers(void)
        if (net_mode == Net_client)
                printf("blktrace: connecting to %s\n", hostname);
 
-       setup_buts();
+       if (setup_buts())
+               return 1;
 
        if (use_tracer_devpaths()) {
                if (setup_tracer_devpaths())
@@ -2650,15 +2718,83 @@ static int run_tracers(void)
        return 0;
 }
 
+static cpu_set_t *get_online_cpus(void)
+{
+       FILE *cpus;
+       cpu_set_t *set;
+       size_t alloc_size;
+       int cpuid, prevcpuid = -1;
+       char nextch;
+       int n, ncpu, curcpu = 0;
+       int *cpu_nums;
+
+       ncpu = sysconf(_SC_NPROCESSORS_CONF);
+       if (ncpu < 0)
+               return NULL;
+
+       cpu_nums = malloc(sizeof(int)*ncpu);
+       if (!cpu_nums) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       /*
+        * There is no way to easily get maximum CPU number. So we have to
+        * parse the file first to find it out and then create appropriate
+        * cpuset
+        */
+       cpus = my_fopen("/sys/devices/system/cpu/online", "r");
+       for (;;) {
+               n = fscanf(cpus, "%d%c", &cpuid, &nextch);
+               if (n <= 0)
+                       break;
+               if (n == 2 && nextch == '-') {
+                       prevcpuid = cpuid;
+                       continue;
+               }
+               if (prevcpuid == -1)
+                       prevcpuid = cpuid;
+               while (prevcpuid <= cpuid) {
+                       /* More CPUs listed than configured? */
+                       if (curcpu >= ncpu) {
+                               errno = EINVAL;
+                               return NULL;
+                       }
+                       cpu_nums[curcpu++] = prevcpuid++;
+               }
+               prevcpuid = -1;
+       }
+       fclose(cpus);
+
+       ncpu = curcpu;
+       max_cpus = cpu_nums[ncpu - 1] + 1;
+
+       /* Now that we have maximum cpu number, create a cpuset */
+       set = CPU_ALLOC(max_cpus);
+       if (!set) {
+               errno = ENOMEM;
+               return NULL;
+       }
+       alloc_size = CPU_ALLOC_SIZE(max_cpus);
+       CPU_ZERO_S(alloc_size, set);
+
+       for (curcpu = 0; curcpu < ncpu; curcpu++)
+               CPU_SET_S(cpu_nums[curcpu], alloc_size, set);
+
+       free(cpu_nums);
+
+       return set;
+}
+
 int main(int argc, char *argv[])
 {
        int ret = 0;
 
        setlocale(LC_NUMERIC, "en_US");
        pagesize = getpagesize();
-       ncpus = sysconf(_SC_NPROCESSORS_CONF);
-       if (ncpus < 0) {
-               fprintf(stderr, "sysconf(_SC_NPROCESSORS_CONF) failed %d/%s\n",
+       online_cpus = get_online_cpus();
+       if (!online_cpus) {
+               fprintf(stderr, "cannot get online cpus %d/%s\n",
                        errno, strerror(errno));
                ret = 1;
                goto out;
@@ -2667,6 +2803,7 @@ int main(int argc, char *argv[])
                goto out;
        }
 
+       ncpus = CPU_COUNT_S(CPU_ALLOC_SIZE(max_cpus), online_cpus);
        if (ndevs > 1 && output_name && strcmp(output_name, "-") != 0) {
                fprintf(stderr, "-o not supported with multiple devices\n");
                ret = 1;