<div dir="ltr"><div dir="ltr"><br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Fri, Oct 4, 2019 at 4:35 PM <<a href="mailto:vsk@apple.com">vsk@apple.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div style="overflow-wrap: break-word;"><br><div><br><blockquote type="cite"><div>On Oct 4, 2019, at 3:43 PM, Petr Hosek <<a href="mailto:phosek@google.com" target="_blank">phosek@google.com</a>> wrote:</div><br><div><div dir="ltr" style="font-family:Helvetica;font-size:14px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;text-decoration:none"><div dir="ltr">On Fri, Oct 4, 2019 at 1:44 PM Xinliang David Li <<a href="mailto:davidxl@google.com" target="_blank">davidxl@google.com</a>> wrote:<br></div><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr">I will let Petr chime in explaining more details -- the reply I provided are based on my understanding of 'Reloc' proposal, which may be quite off.<div><br></div><div>David</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Fri, Oct 4, 2019 at 1:28 PM <<a href="mailto:vsk@apple.com" target="_blank">vsk@apple.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div><br><div><br><blockquote type="cite"><div>On Oct 4, 2019, at 10:30 AM, Xinliang David Li <<a href="mailto:xinliangli@gmail.com" target="_blank">xinliangli@gmail.com</a>> wrote:</div><br><div><div dir="ltr">Petr's method is more like 'relocating' the counter memory from static allocated memory to mmapped memory, but better be named 'reloc' method.</div></div></blockquote><div><br></div><div>Thank you, that's a much better name.</div><div><br></div><br><blockquote type="cite"><div><div dir="ltr"><div>Now lets do some comparison:</div><div><br></div><div>1) reloc method allows runtime to mmap from the start of the raw profile, so there is no need to page align the counter data in the file;</div></div></div></blockquote><div><br></div><div>Let me see if I follow. Is the idea to allocate a buffer large enough to contain the entire contiguous raw profile, in each instrumented image? I can see how it would be possible to mmap() a single such buffer onto a raw profile without any extra padding or format changes. However, this buffer allocation entails doubling the memory overhead of the `__llvm_prf_{cnts,data,names}` sections, as these would all need to be copied into the buffer-to-be-mmap'd. This may not work on well on iOS, as the devices we support are quite memory-constrained. The per-process memory limit on iOS is ~1.4GB on many devices -- clients like News.app or Music.app already get very close to this limit.</div><div><br></div><div>Also, with more than one instrumented image (DSOs), I believe page-alignment padding is still necessary so that each in-memory raw profile buffer can be mmap()'d onto a page-aligned file offset.</div><div><br></div><div>Perhaps that is a non-issue, as it can be solved by simply creating N different raw profiles -- one per instrumented image. Actually this should work for both proposed schemes.</div></div></div></blockquote></div></blockquote><div><br></div><div>David's understanding is correct, my plan is to mmap the entire raw profile from the start which means that there's no requirement for page alignment. Alternative would be to mmap only a page-aligned portion of the raw file of file that contains counters, i.e. we would mmap [__llvm_prf_cnts & -PAGE_SIZE, (__llvm_prf_cnts + __llvm_prf_cnts_length + PAGE_SIZE - 1) & -PAGE_SIZE). There's no need for buffer allocation, we can simply mmap the file directly as read/writable region.</div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div><div><blockquote type="cite"><div><div dir="ltr"><div>2) reloc method does have more instrumentation overhead, however it is not as large as we think.</div><div>   a) the bias load can be done at the start of the function</div><div>   b) on x86, the addressing mode kicks in, there is actually no size difference in terms of updating:</div><div>        addq    $1, counter+16(%rip)</div><div>   vs</div><div>       addq    $1, counter+16(%rax)</div><div><br></div><div> <span> </span>both are 7 bytes long.</div><div><br></div><div>so the overhead will be a few bytes more per function.</div></div></div></blockquote><div><br></div><div>I see, thanks for this correction!</div></div></div></blockquote></div></blockquote><div><br></div><div>Thanks for the suggestion David! The prototype implementation I have loads the bias/reloc on every counter update but loading the bias/reloc at the start of the function is going to be more efficient. I'll update the implementation and try to get some numbers by compiling instrumented Clang.<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div><div><blockquote type="cite"><div><div dir="ltr"><div>3) reloc method produces smaller sized raw profile</div></div></div></blockquote><div><br></div><div>I'm interested in understanding how major of an issue this is for Google's use cases. On iOS/macOS, the worst case overhead of section alignment padding is < 32K/8K respectively per image (due to 16K/4K pages). What's the expected file size overhead for Google? Is a much larger page size in use? If so, is switching to a smaller page size when running instrumented binaries an option?</div><div><br></div><div>In addition to understanding this, per my last comment to Petr, I would also like to understand the specific security concerns w.r.t mapping a section onto a file. If this is something Darwin should not allow, I would like to let our kernel team know!</div></div></div></blockquote></div></blockquote><div><br></div><div>It's not a specific security concern, rather it's a consequence of Fuchsia's design/capability model. In Fuchsia, the address space (sub)regions are objects which are accessed and "operated on" through handles (capabilities). When loading modules (executables and DSOs), our dynamic linker creates a new subregion for each one, mmaps individual segments into those subregions and then closes all subregion handles. That doesn't unmap those files, but it means that the address space layout can never be modified because there's no way to recover those handles. That's a nice property because it means that all module mapping is immutable (e.g. it's impossible to make any of the executable segments writable or even readable if you use execute-only memory on AArch64). However, it also means that we cannot mmap anything over those segments as would be needed for the continuous mode. The only way to make this work is to avoid closing the subregion handles and keep those for later use, but that would also make our dynamic linker a potential attack vector.</div><div><br></div><div>More generally, relying on the overmap behavior means tying the implementation to the OS-specific API which is available on Linux and Darwin, but may not be available on other OSes (e.g. I'm not even sure if this is possible on Windows). It'd be nice to implement a solution that could be made to work on all OSes.</div></div></div></div></blockquote><div><br></div><div>Ok, I'm convinced that the reloc mode is the more portable approach. Per David's comments (the one about not actually emitting `__llvm_prf_cnts` in the binary //especially//, although I haven't quoted it here), reloc mode can probably be implemented reasonably efficiently on Linux/Darwin. The runtime can inspect `__llvm_prf_data` to determine the number of counters, mmap() a sufficient amount of memory, and then update the CounterPtr fields of all the data entries. This means the instrumentation would need to look like: <a href="https://godbolt.org/z/-PvObE" target="_blank">https://godbolt.org/z/-PvObE</a>.</div><div><br></div></div></div></blockquote><div><br></div><div>incr3 version looks great. </div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div style="overflow-wrap: break-word;"><div><div></div><div>@Petr I'd be happy to work together on this. Let me know if you'd like any help prototyping/testing, perhaps we can split up some of the work.</div><div><br></div><div>One thing we haven't really resolved is the conversation about not changing the raw profile format. Fwiw, I don't think avoiding changes to the raw profile format should be a design goal, as the point of having separate raw/indexed formats is that the raw format should be cheap to rev. I think that I've demonstrated that the file size overhead from adding section alignment padding is small: I suspect that getting rid of this would introduce a considerable amount of complexity to any kind of mmap()-mode implementation.</div><div><br></div></div></div></blockquote><div><br></div><div>if there is a need to change raw format to make things more efficient, go for it.</div><div><br></div><div>David</div><div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div style="overflow-wrap: break-word;"><div><div></div><div>Anyway, I'm excited to see your results. Please keep me in the loop!</div><div><br></div><div>thanks,</div><div>vedant</div><div><br></div><br><blockquote type="cite"><div><div dir="ltr" style="font-family:Helvetica;font-size:14px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;text-decoration:none"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div><div><blockquote type="cite"><div><div dir="ltr"><div>4) The continuous method has the advantage of being more efficient in running speed.</div></div></div></blockquote><div><br></div><div>As pointed out above, the reduced memory overhead is another (perhaps more significant) factor.</div><div><br></div><div><br></div><blockquote type="cite"><div><div dir="ltr"><div>Also, I believe both methods should support on-line merging -- as the profile update happens 'In-place' -- the online merging code can be simplified (no locking, reading, updating are needed).</div></div></div></blockquote><div><br></div><div>Thanks for pointing this out. In my last experiment, I added a `while (true) { print-counters; sleep(...) }` loop to `darwin-proof-of-concept.c` after it set up the mmap(), and then edited the counter section of the profile using a hex editor. The updates did not appear in the `proof-of-concept` process. But perhaps there is a more sophisticated way to share the physical pages backing `__llvm_prf_cnts` between processes, while they are also mapped onto the filesystem. I need to run more experiments to understand this. </div><div><br></div><div>vedant</div><div><br></div><br><blockquote type="cite"><div><div dir="ltr"><div><br></div><div>David</div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div><div><br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, Oct 3, 2019 at 4:18 PM Vedant Kumar via Phabricator via llvm-commits <<a href="mailto:llvm-commits@lists.llvm.org" target="_blank">llvm-commits@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">vsk added a comment.<br><br>In D68351#1693693 <<a href="https://reviews.llvm.org/D68351#1693693" rel="noreferrer" target="_blank">https://reviews.llvm.org/D68351#1693693</a>>, @phosek wrote:<br><br>> In D68351#1693307 <<a href="https://reviews.llvm.org/D68351#1693307" rel="noreferrer" target="_blank">https://reviews.llvm.org/D68351#1693307</a>>, @davidxl wrote:<br>><br>> > +petr who has similar needs for Fuchia platform<br>><br>><br>> Thank you for including me, we have exactly the same use case in Fuchsia and this is the solution I've initially considered and been experimenting with locally based on the discussion with @davidxl. However, after internal discussion we've decided against this solution for two reasons:<br>><br>> 1. This requires the ability to mmap the output file over the (portion of) binary which is something we don't allow in Fuchsia for security reasons; once all modules have been mapped in by the dynamic linker, we don't allow any further changes to their mapping and using this solution would require special dynamic linker which is possible but highly undesirable.<br>> 2. It introduces bloat to both the binary and the output file. It also complicates the runtime implementation due to alignment and padding requirements.<br>><br>>   The alternative solution we've came up and that I've now been implementing is to change the instrumentation to allow extra level of indirection. Concretely, the instrumentation conceptually does the following: `c = __profc_foo[idx]; c++; __profc_foo[idx] = c`. We'd like to change this `c = *(&__profc[idx] + *bias); c++; *(&__profc[idx] + *bias) = c` where `bias` is a global variable set by the runtime to be the offset between the `__llvm_prf_cnts` section and the corresponding location in the file that's mmapped in. Initially, that offset would be `0` which would result in exactly the same behavior as today, but the runtime can mmap the output file into address space and then change the offset to make the counters be continuously updated.<br>><br>>   The advantage of this solution is that there are no changes needed to the profile format. It also doesn't require mmapping the output file over the binary, the output file can be mmapped anywhere in the address space. The disadvantage is extra overhead since instrumentation is going to be slightly more complicated, although we don't have any numbers yet to quantify how much slower it's going to be. The implementation should be fairly minimal and my tentative plan was to gate it on compiler switch, so it wouldn't affect existing in any way (modulo introducing one new variable in the runtime to hold the bias). I'm hoping to have the prototype ready and uploaded for review within the next few days. What's your opinion on this idea? Would this be something that you'd be interested in as an alternative approach?<br><br><br>@phosek thank you for sharing this alternative. I hope you don't mind my calling this the bias method, after the term in the proposed instrumentation.<br><br>The TLDR is that I don't think the bias method is a good fit for Linux/Darwin. Imho, this method won't reduce the complexity of continuous mode, and its instrumentation overhead is likely to outweigh the savings from reduced section alignment. Let me try and justify these claims :).<br><br>First and most critically, note that (on Linux & Darwin, at least), mmap() requires that the file `offset` argument be page-aligned. So, I don't believe there's a way to avoid changing the raw profile format, or to avoid the concomitant complexity necessary to calculate padding bytes in the runtime. I don't see how the bias method solves this problem: this seems to be a fundamental limitation of mmap().<br><br>Second, note that the size overhead of counter increment instrumentation dwarfs the alignment overhead for `__llvm_prf_{cnts,data}` for all but the smallest of programs. Assuming 16K pages, in the worst case, the section alignment costs 32K per image. Assuming an increment can be encoded in 7 bytes as `incq l___profc_main(%rip)`, this section alignment cost is equivalent to ~4700 increments. For comparison, this is roughly the number of counter increments in //FileCheck.cpp.o//. If the size overhead of counter instrumentation were to double, as I believe it would with the bias method, it would rapidly erase the memory savings from eliminating the section alignment requirement.<br><br>Third, I'm not sure I understand the security constraints in Fuchsia that make it undesirable to map the contents of a section onto a file. Is the concern that an attacker process may update the file, thereby changing the in-memory contents of the section? I'll note that on Darwin, at least, the kernel does not permit this kind of sharing. If an attacker process modifies a file on disk that has the in-memory contents of a section MAP_SHARED over it, the in-memory contents of the mapped section are not changed (I verified this by making a small modification to `darwin-proof-of-concept.c`). If there is some security aspect to the problem I'm missing here, could you please elaborate on it? I'm hoping that the bias method will not be necessary to support continuous mode on Fuchsia, but it seems this depends on the answer to the security question. Either way, istm that the high level approach taken in this patch as-written is a better fit for Linux/Darwin, and moreover probably provides support that can be used to implement the bias method.<br><br>I appreciate your feedback and think we can definitely work together to build some kind of common solution!<br><br><br>CHANGES SINCE LAST ACTION<br> <span> </span><a href="https://reviews.llvm.org/D68351/new/" rel="noreferrer" target="_blank">https://reviews.llvm.org/D68351/new/</a><br><br><a href="https://reviews.llvm.org/D68351" rel="noreferrer" target="_blank">https://reviews.llvm.org/D68351</a><br><br><br><br>_______________________________________________<br>llvm-commits mailing list<br><a href="mailto:llvm-commits@lists.llvm.org" target="_blank">llvm-commits@lists.llvm.org</a><br><a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits</a></blockquote></div></div></blockquote></div></div></blockquote></div></blockquote></div></div></div></blockquote></div><br></div></blockquote></div></div>