| #!/usr/bin/env ruby |
| # Copyright (c) 2023, gperftools Contributors |
| # All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions are |
| # met: |
| # |
| # * Redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer. |
| # * Redistributions in binary form must reproduce the above |
| # copyright notice, this list of conditions and the following disclaimer |
| # in the documentation and/or other materials provided with the |
| # distribution. |
| # * Neither the name of Google Inc. nor the names of its |
| # contributors may be used to endorse or promote products derived from |
| # this software without specific prior written permission. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| require 'digest' |
| |
| # This is main logic. If you want to add new ucontext-to-pc accessor, add it here |
| def dwm!(d) |
| d.template "NetBSD has really nice portable macros", :_UC_MACHINE_PC do |uc| |
| "_UC_MACHINE_PC(#{uc})" |
| end |
| |
| d.with_prefix "uc_mcontext." do |
| # first arg is ifdef, second is field (with prefix prepended) and third is comment |
| d.ifdef :REG_PC, "gregs[REG_PC]", "Solaris/x86" |
| d.ifdef :REG_EIP, "gregs[REG_EIP]", "Linux/i386" |
| d.ifdef :REG_RIP, "gregs[REG_RIP]", "Linux/amd64" |
| d.field "sc_ip", "Linux/ia64" |
| d.field "__pc", "Linux/loongarch64" |
| d.field "pc", "Linux/{mips,aarch64}" |
| d.ifdef :PT_NIP, "uc_regs->gregs[PT_NIP]", "Linux/ppc" |
| d.ifdef :PT_NIP, "gp_regs[PT_NIP]", "Linux/ppc" |
| d.ifdef :REG_PC, "__gregs[REG_PC]", "Linux/riscv" |
| d.field "psw.addr", "Linux/s390" |
| d.field "arm_pc", "Linux/arm (32-bit; legacy)" |
| d.field "mc_eip", "FreeBSD/i386" |
| d.field "mc_srr0", "FreeBSD/ppc" |
| d.field "mc_rip", "FreeBSD/x86_64" |
| end |
| |
| d.with_prefix "uc_mcontext->" do |
| d.field "ss.eip", "OS X (i386, <=10.4)" |
| d.field "__ss.__eip", "OS X (i386, >=10.5)" |
| d.field "ss.rip", "OS X (x86_64)" |
| d.field "__ss.__rip", "OS X (>=10.5 [untested])" |
| d.field "ss.srr0", "OS X (ppc, ppc64 [untested])" |
| d.field "__ss.__srr0", "OS X (>=10.5 [untested])" |
| d.field "__ss.__pc", "OS X (arm64)" |
| end |
| |
| d.field "sc_eip", "OpenBSD/i386" |
| d.field "sc_rip", "OpenBSD/x86_64" |
| end |
| |
| # this is generator logic |
| class Definer |
| def initialize |
| @prefix = "" |
| @accessors = {} |
| puts(<<HERE) |
| // -*- eval: (read-only-mode) -*- |
| // WARNING: this file is autogenerated. |
| // Change and run src/gen_getpc.rb if you want to |
| // update. (And submit both files) |
| |
| // What this file does? We have several possible ways of fetching PC |
| // (program counter) of signal's ucontext. We explicitly choose to |
| // avoid ifdef-ing specific OSes (or even specific versions), to |
| // increase our chances that stuff simply works. Comments below refer |
| // to OS/architecture combos for documentation purposes, but what |
| // works is what is used. |
| |
| // How it does it? It uses lightweight C++ template magic where |
| // "wrong" ucontext_t{nullptr}-><field access> combos are |
| // automagically filtered out (via SFINAE). |
| |
| // Each known case is represented as a template class. For SFINAE |
| // reasons we masquerade ucontext_t type behind U template |
| // parameter. And we also parameterize by parent class. This allows us |
| // to arrange all template instantiations in a single ordered chain of |
| // inheritance. See RawUCToPC below. |
| |
| // Note, we do anticipate that most times exactly one of those access |
| // methods works. But we're prepared there could be several. In |
| // particular, according to previous comments Solaris/x86 also has |
| // REG_RIP defined, but it is somehow wrong. So we're careful about |
| // preserving specific order. We couldn't handle this "multiplicity" |
| // aspect in pure C++, so we use code generation. |
| |
| namespace internal { |
| |
| struct Empty { |
| #ifdef DEFINE_TRIVIAL_GET |
| #define HAVE_TRIVIAL_GET |
| // special thing for stacktrace_generic_fp-inl which wants no-op case |
| static void* Get(...) { |
| return nullptr; |
| } |
| #endif |
| }; |
| HERE |
| end |
| |
| def with_prefix(prefix) |
| old_prefix = @prefix |
| @prefix = @prefix.dup + prefix |
| yield |
| ensure |
| @prefix = old_prefix |
| end |
| |
| def ifdef define, field, comment |
| field field, comment, define |
| end |
| |
| def template comment, define = nil, &block |
| field block, comment, define |
| end |
| |
| def field field, comment, define = nil |
| tmpl = if field.kind_of? Proc |
| raise unless @prefix.empty? |
| field |
| else |
| proc do |uc| |
| "#{uc}->#{@prefix + field}" |
| end |
| end |
| fingerprint = Digest::MD5.hexdigest(tmpl["%"] + "@" + comment)[0,8] |
| |
| maybe_open_ifdef = "\n#ifdef #{define}" if define |
| maybe_close_ifdef = "\n#endif // #{define}" if define |
| |
| raise "conflict!" if @accessors.include? fingerprint |
| |
| if define |
| @accessors[fingerprint] = comment + " (with #ifdef #{define})" |
| else |
| @accessors[fingerprint] = comment |
| end |
| |
| puts(<<HERE) |
| |
| // #{comment} |
| template <class U, class P, class = void> |
| struct get_#{fingerprint} : public P { |
| };#{maybe_open_ifdef} |
| template <class U, class P> |
| struct get_#{fingerprint}<U, P, void_t<decltype(#{tmpl["((U*){})"]})>> : public P { |
| static void* Get(const U* uc) { |
| // #{comment} |
| return (void*)(#{tmpl[:uc]}); |
| } |
| };#{maybe_close_ifdef} |
| HERE |
| end |
| |
| def finalize! |
| puts |
| puts(<<HERE) |
| inline void* RawUCToPC(const ucontext_t* uc) { |
| HERE |
| prev = "Empty" |
| @accessors.each_pair.reverse_each do |(fingerprint, comment)| |
| puts " // #{comment}" |
| puts " using g_#{fingerprint} = get_#{fingerprint}<ucontext_t, #{prev}>;" |
| prev = "g_#{fingerprint}" |
| end |
| puts(<<HERE) |
| return #{prev}::Get(uc); |
| } |
| HERE |
| puts |
| puts("} // namespace internal") |
| end |
| end |
| |
| path = File.join(File.dirname(__FILE__), "getpc-inl.h") |
| STDOUT.reopen(IO.popen(["tee", path], "w")) |
| |
| Definer.new.tap {|instance| dwm!(instance)}.finalize! |