From: Paul Duncan Date: 2005-08-19T04:36:36+09:00 Subject: [PATCH] FW: Ruby 1.8.2 DTrace Support --E+IgQzR66AIOcbjA Content-Type: multipart/mixed; boundary="cs5saTBZh7UZl2eX" Content-Disposition: inline --cs5saTBZh7UZl2eX Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-Transfer-Encoding: quoted-printable Hi everyone, Richard Lowe (richlowe@richlowe.net) wrote this patch to add a DTrace provider into Ruby. He's not subscribed to ruby-core, so he asked me to forward the patch and his description on to the list. The patch is attached, and his description is below. I don't know anything about DTrace or the patch, so please forward questions or comments directly to him (discussion can stay here, he can read the archives like anyone else :D). Hopefully this is useful to some people. He showed me the output and it looked pretty interesting. ----- Forwarded message from Richard Lowe ----- Date: Thu, 18 Aug 2005 10:59:33 -0400 User-Agent: Mozilla/5.0 (X11; U; SunOS i86pc; en-US; rv:1.7) Gecko/20050704 =46rom: Richard Lowe To: pabs@pablotron.org Subject: DTrace support. [I'm not subscribed, so please CC me on replies] I spent the last couple of days hacking a DTrace (http://www.sun.com/bigadmin/content/dtrace/) provider into ruby. What I have right now is basically functioning, but I don't have the understanding of the ruby internals necessary to improve upon it further or maintain it. I don't want to leave it to rot on my disk, so if anybody wants to use it, clean it up, maintain it, integrate it, or whatever, the patch (against ruby 1.8.2) is attached. Solaris patch(1) fails to apply it because of the file creation, GNU patch applies it just fine (removing the 'dtrace.d' hunk from the diff, and creating that file by hand should let Solaris patch(1) apply it). I hope it's useful to somebody :) I've tested it under Solaris Express on x86, 64bit builds of ruby will need the the -32 argument to dtrace in the Makefile replaced with -64. (I couldn't find a sensible way to autoconfize that). It defines two probes, ruby$pid:::function-entry (called before a method call is entered), and ruby$pid:::function-return (called after a method call returns). (where $pid is the process ID of a running ruby) The arguments to each are the classname, methodname, source file name, and line number. ----- End forwarded message ----- --=20 Paul Duncan OpenPGP Key ID: 0x82C29562 http://www.pablotron.org/ http://www.paulduncan.org/ --cs5saTBZh7UZl2eX Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="ruby-1.8.2-dtrace.diff" Content-Transfer-Encoding: quoted-printable Index: Makefile.in =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- Makefile.in (.../clean) (revision 23) +++ Makefile.in (.../diff-tree) (revision 23) @@ -103,6 +103,8 @@ version.@OBJEXT@ \ $(MISSING) =20 +DTRACE_OBJS =3D @DTRACE_OBJS@ + MANTYPE =3D @MANTYPE@ =20 SCRIPT_ARGS =3D --dest-dir=3D"$(DESTDIR)" \ @@ -124,13 +126,13 @@ # We must `rm' the library each time this rule is invoked because "updatin= g" a # MAB library on Apple/NeXT (see --enable-fat-binary in configure) is not # supported. -$(LIBRUBY_A): $(OBJS) $(DMYEXT) +$(LIBRUBY_A): $(OBJS) $(DMYEXT) $(DTRACE_OBJS) @rm -f $@ - @AR@ rcu $@ $(OBJS) $(DMYEXT) + @AR@ rcu $@ $(DTRACE_OBJS) $(OBJS) $(DMYEXT) @-@RANLIB@ $@ 2> /dev/null || true =20 -$(LIBRUBY_SO): $(OBJS) $(DLDOBJS) miniruby$(EXEEXT) $(PREP) - $(LDSHARED) $(DLDFLAGS) $(OBJS) $(DLDOBJS) $(SOLIBS) -o $@ +$(LIBRUBY_SO): $(OBJS) $(DLDOBJS) miniruby$(EXEEXT) $(PREP) $(DTRACE_OBJS) + $(LDSHARED) $(DLDFLAGS) $(DTRACE_OBJS) $(OBJS) $(DLDOBJS) $(SOLIBS) -o $@ @-$(MINIRUBY) -e 'ARGV.each{|link| File.delete link if File.exist? link;= \ File.symlink "$(LIBRUBY_SO)", link}' \ $(LIBRUBY_ALIASES) || true @@ -378,3 +380,6 @@ missing.h env.h node.h st.h util.h version.@OBJEXT@: version.c ruby.h config.h defines.h intern.h missing.h \ version.h + +dtrace.@OBJEXT@: dtrace.d $(OBJS) $(MAINOBJ)=20 + /usr/sbin/dtrace -G -32 -o $@ -s dtrace.d $(OBJS) $(MAINOBJ) Index: configure.in =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- configure.in (.../clean) (revision 23) +++ configure.in (.../diff-tree) (revision 23) @@ -383,6 +383,12 @@ sys/mkdev.h sys/utime.h netinet/in_systm.h float.h ieeefp.h pthread.h \ ucontext.h intrinsics.h) =20 + +AC_CHECK_HEADER(sys/sdt.h) +if test "$ac_cv_header_sys_sdt_h" =3D=3D "yes"; then + AC_DEFINE(HAVE_SDT_H) +fi + dnl Checks for typedefs, structures, and compiler characteristics. AC_TYPE_UID_T AC_TYPE_SIZE_T @@ -415,6 +421,18 @@ AC_DEFINE(USE_SETREUID) AC_DEFINE(USE_SETREGID) fi + +AC_ARG_ENABLE(dtrace, + [ --enable-dtrace Enable DTrace support.], + [enable_dtrace=3D$enableval]) +if test "$enable_dtrace" =3D=3D "yes" -a "$ac_cv_header_sys_sdt_h" =3D=3D = "yes"; then + AC_DEFINE(ENABLE_DTRACE) + DTRACE_OBJS=3D"dtrace.o" +else + DTRACE_OBJS=3D"" +fi +AC_SUBST(DTRACE_OBJS) + AC_STRUCT_TIMEZONE AC_CACHE_CHECK(for struct tm.tm_gmtoff, rb_cv_member_struct_tm_tm_gmtoff, [AC_TRY_COMPILE([#include ], Index: configure =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- configure (.../clean) (revision 23) +++ configure (.../diff-tree) (revision 23) @@ -308,7 +308,7 @@ # include #endif" =20 -ac_subst_vars=3D'SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE= _VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transf= orm_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir locals= tatedir libdir includedir oldincludedir infodir mandir build_alias host_ali= as target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS MAJOR MINOR TEENY build buil= d_cpu build_vendor build_os host host_cpu host_vendor host_os target target= _cpu target_vendor target_os CC ac_ct_CC CFLAGS LDFLAGS CPPFLAGS EXEEXT OBJ= EXT CPP EGREP GNU_LD CPPOUTFILE OUTFLAG YACC RANLIB ac_ct_RANLIB AR ac_ct_A= R NM ac_ct_NM WINDRES ac_ct_WINDRES DLLWRAP ac_ct_DLLWRAP LN_S SET_MAKE LIB= OBJS ALLOCA DLDFLAGS ARCH_FLAG STATIC CCDLFLAGS LDSHARED DLEXT DLEXT2 LIBEX= T LINK_SO LIBPATHFLAG RPATHFLAG TRY_LINK STRIP EXTSTATIC setup MINIRUBY PRE= P ARCHFILE RDOCTARGET XCFLAGS XLDFLAGS LIBRUBY_LDSHARED LIBRUBY_DLDFLAGS RU= BY_INSTALL_NAME rubyw_install_name RUBYW_INSTALL_NAME RUBY_SO_NAME LIBRUBY_= A LIBRUBY_SO LIBRUBY_ALIASES LIBRUBY LIBRUBYARG LIBRUBYARG_STATIC LIBRUBYAR= G_SHARED SOLIBS DLDLIBS ENABLE_SHARED MAINLIBS COMMON_LIBS COMMON_MACROS CO= MMON_HEADERS EXPORT_PREFIX MAKEFILES arch sitearch sitedir configure_args N= ROFF MANTYPE LTLIBOBJS' +ac_subst_vars=3D'SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE= _VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transf= orm_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir locals= tatedir libdir includedir oldincludedir infodir mandir build_alias host_ali= as target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS MAJOR MINOR TEENY build buil= d_cpu build_vendor build_os host host_cpu host_vendor host_os target target= _cpu target_vendor target_os CC ac_ct_CC CFLAGS LDFLAGS CPPFLAGS EXEEXT OBJ= EXT CPP EGREP GNU_LD CPPOUTFILE OUTFLAG YACC RANLIB ac_ct_RANLIB AR ac_ct_A= R NM ac_ct_NM WINDRES ac_ct_WINDRES DLLWRAP ac_ct_DLLWRAP LN_S SET_MAKE LIB= OBJS ALLOCA DTRACE_OBJS DLDFLAGS ARCH_FLAG STATIC CCDLFLAGS LDSHARED DLEXT = DLEXT2 LIBEXT LINK_SO LIBPATHFLAG RPATHFLAG TRY_LINK STRIP EXTSTATIC setup = MINIRUBY PREP ARCHFILE RDOCTARGET XCFLAGS XLDFLAGS LIBRUBY_LDSHARED LIBRUBY= _DLDFLAGS RUBY_INSTALL_NAME rubyw_install_name RUBYW_INSTALL_NAME RUBY_SO_N= AME LIBRUBY_A LIBRUBY_SO LIBRUBY_ALIASES LIBRUBY LIBRUBYARG LIBRUBYARG_STAT= IC LIBRUBYARG_SHARED SOLIBS DLDLIBS ENABLE_SHARED MAINLIBS COMMON_LIBS COMM= ON_MACROS COMMON_HEADERS EXPORT_PREFIX MAKEFILES arch sitearch sitedir conf= igure_args NROFF MANTYPE LTLIBOBJS' ac_subst_files=3D'' =20 # Initialize some variables set by options. @@ -863,6 +863,7 @@ --disable-largefile omit support for large files --enable-pthread use pthread library. --enable-setreuid use setreuid()/setregid() according to need even= if obsolete. + --enable-dtrace Enable DTrace support. --disable-rpath embed run path into extension libraries. --enable-shared build a shared library for Ruby. --enable-install-doc build and install rdoc indexes during install @@ -10146,6 +10147,152 @@ done =20 =20 + +if test "${ac_cv_header_sys_sdt_h+set}" =3D set; then + echo "$as_me:$LINENO: checking for sys/sdt.h" >&5 +echo $ECHO_N "checking for sys/sdt.h... $ECHO_C" >&6 +if test "${ac_cv_header_sys_sdt_h+set}" =3D set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: $ac_cv_header_sys_sdt_h" >&5 +echo "${ECHO_T}$ac_cv_header_sys_sdt_h" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking sys/sdt.h usability" >&5 +echo $ECHO_N "checking sys/sdt.h usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=3D$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? =3D $ac_status" >&5 + (exit $ac_status); } && + { ac_try=3D'test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=3D$? + echo "$as_me:$LINENO: \$? =3D $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try=3D'test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=3D$? + echo "$as_me:$LINENO: \$? =3D $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=3Dyes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=3Dno +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking sys/sdt.h presence" >&5 +echo $ECHO_N "checking sys/sdt.h presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=3D$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? =3D $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=3D$ac_c_preproc_warn_flag + ac_cpp_err=3D$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err=3D + fi +else + ac_cpp_err=3Dyes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=3Dyes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=3Dno +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: sys/sdt.h: accepted by the compiler, = rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: sys/sdt.h: accepted by the compiler, rejected by th= e preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: sys/sdt.h: proceeding with the compil= er's result" >&5 +echo "$as_me: WARNING: sys/sdt.h: proceeding with the compiler's result" >= &2;} + ac_header_preproc=3Dyes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: sys/sdt.h: present but cannot be comp= iled" >&5 +echo "$as_me: WARNING: sys/sdt.h: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: sys/sdt.h: check for missing prer= equisite headers?" >&5 +echo "$as_me: WARNING: sys/sdt.h: check for missing prerequisite heade= rs?" >&2;} + { echo "$as_me:$LINENO: WARNING: sys/sdt.h: see the Autoconf documenta= tion" >&5 +echo "$as_me: WARNING: sys/sdt.h: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: sys/sdt.h: section \"Present But = Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: sys/sdt.h: section \"Present But Cannot Be Comp= iled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: sys/sdt.h: proceeding with the prepro= cessor's result" >&5 +echo "$as_me: WARNING: sys/sdt.h: proceeding with the preprocessor's resul= t" >&2;} + { echo "$as_me:$LINENO: WARNING: sys/sdt.h: in the future, the compile= r will take precedence" >&5 +echo "$as_me: WARNING: sys/sdt.h: in the future, the compiler will take pr= ecedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------ ## +## Report this to the AC_PACKAGE_NAME lists. ## +## ------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for sys/sdt.h" >&5 +echo $ECHO_N "checking for sys/sdt.h... $ECHO_C" >&6 +if test "${ac_cv_header_sys_sdt_h+set}" =3D set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_header_sys_sdt_h=3D$ac_header_preproc +fi +echo "$as_me:$LINENO: result: $ac_cv_header_sys_sdt_h" >&5 +echo "${ECHO_T}$ac_cv_header_sys_sdt_h" >&6 + +fi + + +if test "$ac_cv_header_sys_sdt_h" =3D=3D "yes"; then + cat >>confdefs.h <<\_ACEOF +#define HAVE_SDT_H 1 +_ACEOF + +fi + echo "$as_me:$LINENO: checking for uid_t in sys/types.h" >&5 echo $ECHO_N "checking for uid_t in sys/types.h... $ECHO_C" >&6 if test "${ac_cv_type_uid_t+set}" =3D set; then @@ -11773,6 +11920,23 @@ _ACEOF =20 fi + +# Check whether --enable-dtrace or --disable-dtrace was given. +if test "${enable_dtrace+set}" =3D set; then + enableval=3D"$enable_dtrace" + enable_dtrace=3D$enableval +fi; +if test "$enable_dtrace" =3D=3D "yes" -a "$ac_cv_header_sys_sdt_h" =3D=3D = "yes"; then + cat >>confdefs.h <<\_ACEOF +#define ENABLE_DTRACE 1 +_ACEOF + + DTRACE_OBJS=3D"dtrace.o" +else + DTRACE_OBJS=3D"" +fi + + echo "$as_me:$LINENO: checking whether struct tm is in sys/time.h or time.= h" >&5 echo $ECHO_N "checking whether struct tm is in sys/time.h or time.h... $EC= HO_C" >&6 if test "${ac_cv_struct_tm+set}" =3D set; then @@ -15810,6 +15974,7 @@ s,@SET_MAKE@,$SET_MAKE,;t t s,@LIBOBJS@,$LIBOBJS,;t t s,@ALLOCA@,$ALLOCA,;t t +s,@DTRACE_OBJS@,$DTRACE_OBJS,;t t s,@DLDFLAGS@,$DLDFLAGS,;t t s,@ARCH_FLAG@,$ARCH_FLAG,;t t s,@STATIC@,$STATIC,;t t Index: eval.c =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- eval.c (.../clean) (revision 23) +++ eval.c (.../diff-tree) (revision 23) @@ -136,6 +136,10 @@ =20 #include =20 +#ifdef ENABLE_DTRACE +#include +#endif /* ENABLE_DTRACE */ + VALUE rb_cProc; static VALUE rb_cBinding; static VALUE proc_invoke _((VALUE,VALUE,VALUE,VALUE)); @@ -1011,7 +1015,9 @@ #define YIELD_FUNC_AVALUE 1 #define YIELD_FUNC_SVALUE 2 =20 -static VALUE rb_call _((VALUE,VALUE,ID,int,const VALUE*,int)); +/* DTrace cannot instrument a static function */ +VALUE rb_call _((VALUE,VALUE,ID,int,const VALUE*,int)); + static VALUE module_setup _((VALUE,NODE*)); =20 static VALUE massign _((VALUE,NODE*,VALUE,int)); @@ -5689,7 +5695,8 @@ return result; } =20 -static VALUE +/* DTrace cannot instrument a static function */ +VALUE rb_call(klass, recv, mid, argc, argv, scope) VALUE klass, recv; ID mid; @@ -5701,11 +5708,25 @@ int noex; ID id =3D mid; struct cache_entry *ent; + VALUE callret; + char *classname; + char *methodname; =20 if (!klass) { rb_raise(rb_eNotImpError, "method `%s' called on terminated object (0x%lx= )", rb_id2name(mid), recv); } + + classname =3D rb_class2name(klass); + methodname =3D rb_id2name(mid); + =20 +#ifdef ENABLE_DTRACE + /* Don't pass bad pointers into the probe routine */ + if (classname && methodname && ruby_sourcefile) + DTRACE_PROBE4(ruby, function__entry, classname, methodname, + ruby_sourcefile, ruby_sourceline); +#endif /* ENABLE_DTRACE */ + =20 /* is it in the method cache? */ ent =3D cache + EXPR1(klass, mid); if (ent->mid =3D=3D mid && ent->klass =3D=3D klass) { @@ -5740,7 +5761,17 @@ } } =20 - return rb_call0(klass, recv, mid, id, argc, argv, body, noex & NOEX_NO= SUPER); + + callret =3D rb_call0(klass, recv, mid, id, argc, argv, body, noex & NO= EX_NOSUPER); + +#ifdef ENABLE_DTRACE + /* Don't pass bad pointers into the probe routine */ + if (classname && methodname && ruby_sourcefile) + DTRACE_PROBE4(ruby, function__return, classname, methodname, + ruby_sourcefile, ruby_sourceline); +#endif /* ENABLE_DTRACE */ + + return (callret); } =20 VALUE --cs5saTBZh7UZl2eX-- --E+IgQzR66AIOcbjA Content-Type: application/pgp-signature; name="signature.asc" Content-Description: Digital signature Content-Disposition: inline -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.0 (GNU/Linux) iD8DBQFDBOM/zdlT34LClWIRAkn/AJ94nKN8LuybBFN4ZY9MoAoGXsB2qQCgr41D LaidqLvM4hnlQBE4XDDaWNA= =QZvN -----END PGP SIGNATURE----- --E+IgQzR66AIOcbjA--