perf tools: Add FIFO file names as alternative options to --control
[linux-block.git] / tools / perf / util / evlist.c
index e3fa3bf7498a2224d546cec1dfabe34028ad6a63..00593e5f2a9d58c32775d39d72227c9bbc82309c 100644 (file)
@@ -1727,6 +1727,81 @@ struct evsel *perf_evlist__reset_weak_group(struct evlist *evsel_list,
        return leader;
 }
 
+static int evlist__parse_control_fifo(const char *str, int *ctl_fd, int *ctl_fd_ack, bool *ctl_fd_close)
+{
+       char *s, *p;
+       int ret = 0, fd;
+
+       if (strncmp(str, "fifo:", 5))
+               return -EINVAL;
+
+       str += 5;
+       if (!*str || *str == ',')
+               return -EINVAL;
+
+       s = strdup(str);
+       if (!s)
+               return -ENOMEM;
+
+       p = strchr(s, ',');
+       if (p)
+               *p = '\0';
+
+       /*
+        * O_RDWR avoids POLLHUPs which is necessary to allow the other
+        * end of a FIFO to be repeatedly opened and closed.
+        */
+       fd = open(s, O_RDWR | O_NONBLOCK | O_CLOEXEC);
+       if (fd < 0) {
+               pr_err("Failed to open '%s'\n", s);
+               ret = -errno;
+               goto out_free;
+       }
+       *ctl_fd = fd;
+       *ctl_fd_close = true;
+
+       if (p && *++p) {
+               /* O_RDWR | O_NONBLOCK means the other end need not be open */
+               fd = open(p, O_RDWR | O_NONBLOCK | O_CLOEXEC);
+               if (fd < 0) {
+                       pr_err("Failed to open '%s'\n", p);
+                       ret = -errno;
+                       goto out_free;
+               }
+               *ctl_fd_ack = fd;
+       }
+
+out_free:
+       free(s);
+       return ret;
+}
+
+int evlist__parse_control(const char *str, int *ctl_fd, int *ctl_fd_ack, bool *ctl_fd_close)
+{
+       char *comma = NULL, *endptr = NULL;
+
+       *ctl_fd_close = false;
+
+       if (strncmp(str, "fd:", 3))
+               return evlist__parse_control_fifo(str, ctl_fd, ctl_fd_ack, ctl_fd_close);
+
+       *ctl_fd = strtoul(&str[3], &endptr, 0);
+       if (endptr == &str[3])
+               return -EINVAL;
+
+       comma = strchr(str, ',');
+       if (comma) {
+               if (endptr != comma)
+                       return -EINVAL;
+
+               *ctl_fd_ack = strtoul(comma + 1, &endptr, 0);
+               if (endptr == comma + 1 || *endptr != '\0')
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
 int evlist__initialize_ctlfd(struct evlist *evlist, int fd, int ack)
 {
        if (fd == -1) {
@@ -1778,6 +1853,7 @@ static int evlist__ctlfd_recv(struct evlist *evlist, enum evlist_ctl_cmd *cmd,
        char c;
        size_t bytes_read = 0;
 
+       *cmd = EVLIST_CTL_CMD_UNSUPPORTED;
        memset(cmd_data, 0, data_size);
        data_size--;
 
@@ -1789,17 +1865,22 @@ static int evlist__ctlfd_recv(struct evlist *evlist, enum evlist_ctl_cmd *cmd,
                        cmd_data[bytes_read++] = c;
                        if (bytes_read == data_size)
                                break;
-               } else {
-                       if (err == -1)
+                       continue;
+               } else if (err == -1) {
+                       if (errno == EINTR)
+                               continue;
+                       if (errno == EAGAIN || errno == EWOULDBLOCK)
+                               err = 0;
+                       else
                                pr_err("Failed to read from ctlfd %d: %m\n", evlist->ctl_fd.fd);
-                       break;
                }
+               break;
        } while (1);
 
        pr_debug("Message from ctl_fd: \"%s%s\"\n", cmd_data,
                 bytes_read == data_size ? "" : c == '\n' ? "\\n" : "\\0");
 
-       if (err > 0) {
+       if (bytes_read > 0) {
                if (!strncmp(cmd_data, EVLIST_CTL_CMD_ENABLE_TAG,
                             (sizeof(EVLIST_CTL_CMD_ENABLE_TAG)-1))) {
                        *cmd = EVLIST_CTL_CMD_ENABLE;
@@ -1809,7 +1890,7 @@ static int evlist__ctlfd_recv(struct evlist *evlist, enum evlist_ctl_cmd *cmd,
                }
        }
 
-       return err;
+       return bytes_read ? (int)bytes_read : err;
 }
 
 static int evlist__ctlfd_ack(struct evlist *evlist)