On Mon, Mar 17, 2014 at 11:37:47AM -0400, Don Zickus wrote: > On Fri, Mar 14, 2014 at 12:17:05PM +0100, Peter Zijlstra wrote: > > On Thu, Mar 13, 2014 at 04:03:52PM -0400, Don Zickus wrote: > > > Hi Peter, > > > > > > So we found another corner case with MMAP2 interface. I don't think it is > > > a big hurdle to overcome, just wanted a suggestion. > > > > > > Joe ran specjbb2013 (which creates about 10,000 java threads across 9 > > > processes) and our c2c tool turned up some cacheline collision data on > > > libjvm.so. This didn't make sense because you shouldn't be able to write > > > to a shared library. > > > > > > Even worse, our tool said it affected all the java process and a majority > > > of the threads. Which again didn't make sense because this shared library > > > should be local to each pid's memory. > > > > > > Anyway, what we determined is that the shared library had mmap data that > > > was non-zero (because it was backed by a file, libjvm.so). So the > > > assumption was if the major, minor, inode and inode generation numbers > > > were non-zero, this memory segment was shared across processes. > > > > > > So perf setup its map files for the mmap area and then started sampling > > > data > > > addresses. A few hundred HITMs were to a virtual address that fell into > > > the libjvm.so memory segment (which was assumed to be mmap'd across > > > processes). > > > > > > Coalescing all the data suggested that multiple pids/tids were contending > > > for a cacheline in a shared library. > > > > > > After talking with Larry Woodman, we realized when you write to a 'data' > > > or > > > 'bss' segment of a shared library, you incur a COW fault that maps to an > > > anonymous page in the pid's memory. However, perf doesn't see this. > > > > > > So when all the tids start writing to this 'data' or 'bss' segment they > > > generate HITMs within their pid (which is fine). However the tool thinks > > > it affects other pids (which is not fine). > > > > > > My question is, how can our tool determine if a virtual address is private > > > to a pid or not? Originally it had to have a zero for maj, min, ino, and > > > ino gen. But for file map'd libraries this doesn't always work because we > > > don't see COW faults in perf (and we may not want too :-) ). > > > > > > Is there another technique we can use? Perhaps during the reading of > > > /proc/<pid>/maps, if the protection is marked 'p' for private, we just > > > tell > > > the sort algorithm to sort locally to the process but a 's' for shared can > > > be sorted globally based on data addresses? > > > > > > Or something else that tells us that a virtual address has changed its > > > mapping? Thoughts? > > > > Very good indeed; we're missing the protection and flags bits. > > > > How about something like the below; with that you can solve your problem > > by looking at mmap2.flags & MAP_PRIVATE. > > Yes this seemed to work. I attached a slight update to your patch (one > that compiles :-) ). And I will reply to this thread with the tool > changes I used to verify this (in case I did that piece wrong).
And here is the perf tool changes I did to utilize this. Of course this is based on reverting 3090ffb5a2515990182f3f55b0688a7817325488. Cheers, Don diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index b02f3b4..bad58fc 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -1,4 +1,5 @@ #include <linux/types.h> +#include <sys/mman.h> #include "event.h" #include "debug.h" #include "machine.h" @@ -213,6 +214,21 @@ int perf_event__synthesize_mmap_events(struct perf_tool *tool, else event->header.misc = PERF_RECORD_MISC_GUEST_USER; + /* map protection and flags bits */ + event->mmap2.prot = 0; + event->mmap2.flags = 0; + if (prot[0] == 'r') + event->mmap2.prot |= PROT_READ; + if (prot[1] == 'w') + event->mmap2.prot |= PROT_WRITE; + if (prot[2] == 'x') + event->mmap2.prot |= PROT_EXEC; + + if (prot[3] == 's') + event->mmap2.flags |= MAP_SHARED; + else + event->mmap2.flags |= MAP_PRIVATE; + if (prot[2] != 'x') { if (!mmap_data || prot[0] != 'r') continue; @@ -613,12 +629,15 @@ size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp) size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp) { return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 - " %02x:%02x %"PRIu64" %"PRIu64"]: %c %s\n", + " %02x:%02x %"PRIu64" %"PRIu64"]: %c%c%c%c %s\n", event->mmap2.pid, event->mmap2.tid, event->mmap2.start, event->mmap2.len, event->mmap2.pgoff, event->mmap2.maj, event->mmap2.min, event->mmap2.ino, event->mmap2.ino_generation, - (event->header.misc & PERF_RECORD_MISC_MMAP_DATA) ? 'r' : 'x', + (event->mmap2.prot & PROT_READ) ? 'r' : '-', + (event->mmap2.prot & PROT_WRITE) ? 'w' : '-', + (event->mmap2.prot & PROT_EXEC) ? 'x' : '-', + (event->mmap2.flags & MAP_SHARED) ? 's' : 'p', event->mmap2.filename); } diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 38457d4..96bd19c 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -27,6 +27,8 @@ struct mmap2_event { u32 min; u64 ino; u64 ino_generation; + u32 prot; + u32 flags; char filename[PATH_MAX]; }; diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index c8b0fdd..986931e 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -1040,6 +1040,8 @@ int machine__process_mmap2_event(struct machine *machine, event->mmap2.pid, event->mmap2.maj, event->mmap2.min, event->mmap2.ino, event->mmap2.ino_generation, + event->mmap2.prot, + event->mmap2.flags, event->mmap2.filename, type); if (map == NULL) @@ -1085,7 +1087,7 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event map = map__new(&machine->user_dsos, event->mmap.start, event->mmap.len, event->mmap.pgoff, - event->mmap.pid, 0, 0, 0, 0, + event->mmap.pid, 0, 0, 0, 0, 0, 0, event->mmap.filename, type); diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 39cd2d0..f98f8fe 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -51,7 +51,7 @@ void map__init(struct map *map, enum map_type type, struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino, - u64 ino_gen, char *filename, + u64 ino_gen, u32 prot, u32 flags, char *filename, enum map_type type) { struct map *map = malloc(sizeof(*map)); @@ -69,6 +69,8 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, map->min = d_min; map->ino = ino; map->ino_generation = ino_gen; + map->prot = prot; + map->flags = flags; if ((anon || no_dso) && type == MAP__FUNCTION) { snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid); diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index f00f058..8cd0cff 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -35,6 +35,8 @@ struct map { bool referenced; bool erange_warned; u32 priv; + u32 prot; + u32 flags; u64 pgoff; u64 reloc; u32 maj, min; /* only valid for MMAP2 record */ @@ -106,7 +108,7 @@ void map__init(struct map *map, enum map_type type, u64 start, u64 end, u64 pgoff, struct dso *dso); struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino, - u64 ino_gen, + u64 ino_gen, u32 prot, u32 flags, char *filename, enum map_type type); struct map *map__new2(u64 start, struct dso *dso, enum map_type type); void map__delete(struct map *map); -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/