#include <traceevent/event-parse.h>
 #include <linux/hw_breakpoint.h>
 #include <linux/perf_event.h>
+#include <sys/resource.h>
 #include "asm/bug.h"
 #include "evsel.h"
 #include "evlist.h"
        int cpu, thread;
        unsigned long flags = 0;
        int pid = -1, err;
+       enum { NO_CHANGE, SET_TO_MAX, INCREASED_MAX } set_rlimit = NO_CHANGE;
 
        if (evsel->fd == NULL &&
            perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0)
 
                        group_fd = get_group_fd(evsel, cpu, thread);
 
+retry_open:
                        FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr,
                                                                     pid,
                                                                     cpus->map[cpu],
                                err = -errno;
                                goto try_fallback;
                        }
+                       set_rlimit = NO_CHANGE;
                }
        }
 
        return 0;
 
 try_fallback:
+       /*
+        * perf stat needs between 5 and 22 fds per CPU. When we run out
+        * of them try to increase the limits.
+        */
+       if (err == -EMFILE && set_rlimit < INCREASED_MAX) {
+               struct rlimit l;
+               int old_errno = errno;
+
+               if (getrlimit(RLIMIT_NOFILE, &l) == 0) {
+                       if (set_rlimit == NO_CHANGE)
+                               l.rlim_cur = l.rlim_max;
+                       else {
+                               l.rlim_cur = l.rlim_max + 1000;
+                               l.rlim_max = l.rlim_cur;
+                       }
+                       if (setrlimit(RLIMIT_NOFILE, &l) == 0) {
+                               set_rlimit++;
+                               errno = old_errno;
+                               goto retry_open;
+                       }
+               }
+               errno = old_errno;
+       }
+
        if (err != -EINVAL || cpu > 0 || thread > 0)
                goto out_close;