{"id":90,"date":"2016-11-13T23:48:57","date_gmt":"2016-11-13T23:48:57","guid":{"rendered":"http:\/\/clayb.net\/blog\/?p=90"},"modified":"2021-02-16T18:05:33","modified_gmt":"2021-02-16T18:05:33","slug":"debugging-hbase-unit-tests","status":"publish","type":"post","link":"https:\/\/clayb.net\/blog\/debugging-hbase-unit-tests\/","title":{"rendered":"Debugging HBase Unit Tests"},"content":{"rendered":"<p>This is likely an obvious process for those who use IDE&#8217;s and develop in Maven daily but for those who do operations or otherwise need to work on the JUnit tests in HBase only infrequently, here&#8217;s how I worked when submitting a patch for <a href=\"https:\/\/issues.apache.org\/jira\/browse\/HBASE-16700\">HBASE-16700<\/a>.<\/p>\n<h1>First, create your code:<\/h1>\n<p>Here I was adding a <a href=\"https:\/\/blogs.apache.org\/hbase\/entry\/coprocessor_introduction\">MasterObserver coprocessor<\/a> to HBase, so I could work relatively easily writing my code as it was simply one class. I was able to do the following &#8212; very crude &#8212; workflow:<\/p>\n<ol>\n<li>Add the following to my HBase master&#8217;s <tt>hbase-site.xml<\/tt>:<br \/>\n<code>&lt;property&gt;<br \/>\n&lt;name&gt;hbase.coprocessor.master.classes&lt;\/name&gt;<br \/>\n&lt;value&gt;org.apache.hadoop.hbase.security.access.AccessController&lt;\/value&gt;<br \/>\n&lt;\/property&gt;<br \/>\n<\/code><\/li>\n<li><tt>export CLASSPATH=$(hbase classpath)<\/tt><\/li>\n<li><tt>vi &lt;my code&gt;.java<\/tt><\/li>\n<li><tt>javac &lt;my code&gt;.java<\/tt><\/li>\n<li>Copy my class file into my HBase master&#8217;s lib directory<\/li>\n<li>Restart my HBase master<\/li>\n<\/ol>\n<h1>Next, create a test:<\/h1>\n<p>This is the novel part to operators, you simply need to create a file under the relevant directory for the feature you are committing but in traditional Java fashion it will be under <tt>src\/test<\/tt> while your feature will go under <tt>src\/main<\/tt>. HBase has some <a href=\"https:\/\/hbase.apache.org\/book.html#hbase.tests\">guidelines<\/a> on writing a test. Similarly, a useful class for writing HBase-server tests which need a minicluster is <a href=\"https:\/\/hbase.apache.org\/testapidocs\/org\/apache\/hadoop\/hbase\/HBaseTestingUtility.html\">HBaseTestingUtility<\/a>. Remember to write positive and negative tests (prove that your code does what you expect and handles unexpected operations gracefully).<\/p>\n<h2>Testing your test<\/h2>\n<p>To test your test you can ask Maven to run a build and test just your test class via the following: <tt>mvn -X test '-Dtest=org.apache.hadoop.hbase.security.access.TestCoprocessorWhitelistMasterObserver'<\/tt>.<\/p>\n<p>Now, the <tt>-x<\/tt> is not necessary, it runs Maven in debug mode which is useful here. As to see the log output in your test you will want to run it standalone and Maven in debug mode will give you the proper incantation with the classes it built. You will see a line akin to the following while your test is forked off: <tt>Forking command line: \/bin\/sh -c cd hbase\/hbase-server &amp;&amp; \/usr\/lib\/jvm\/jdk1.8.0_101\/jre\/bin\/java -enableassertions -Dhbase.build.id=2016-11-13T22:53:41Z -Xmx2800m -Djava.security.egd=file:\/dev\/.\/urandom -Djava.net.preferIPv4Stack=true -Djava.awt.headless=true -jar hbase\/hbase-server\/target\/surefire\/surefirebooter5454815236698078750.jar hbase\/hbase-server\/target\/surefire\/surefire4890497615179486565tmp hbase\/hbase-server\/target\/surefire\/surefire_09143864480388952525tmp<br \/>\nRunning org.apache.hadoop.hbase.security.access.TestCoprocessorWhitelistMasterObserver<\/tt><\/p>\n<p>This line is useful as you can copy-and-paste it to run your test manually. A particularly useful feature is seeing log output. But also with this line you can attach a debugger too!<\/p>\n<h2>Attaching a debugger<\/h2>\n<p>To attach a debugger, one needs to launch Java with some options for it to wait until the debugger attaches. I was using the particular incantation: <tt>export JAVA_DEBUG='-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000'<\/tt>. I would simply add <tt>$JAVA_DEBUG<\/tt> just after the <tt>\/usr\/bin\/java<\/tt>. Here we ask the process to listen on port 8000 for the debugger to connect (and as one could guess, <tt>suspend=n<\/tt> will not wait for a debugger to connect).<\/p>\n<p>Java ships a command-line debugger (<a href=\"https:\/\/docs.oracle.com\/javase\/7\/docs\/technotes\/tools\/windows\/jdb.html\">jdb<\/a>) but it has no command line history or class tab completion which is a pain. I used Andrew Pimlott&#8217;s <a href=\"https:\/\/github.com\/pimlott\/rlwrap-jdb\">rlwrap-jdb<\/a> to provide these features. I could spin up a debugger with: <tt>CLASSPATH=hbase\/hbase-server\/target\/test-classes\/org\/apache\/hadoop\/hbase\/security\/access\/:hbase\/hbase-server\/target\/ .\/list-java-breakpoints 2&gt;\/dev\/null &gt; breakpoints_file &amp;&amp; .\/rlwrap-jdb --breakpoints-file breakpoints_file jdb -attach 8000<\/tt>.<\/p>\n<h1>Running a debugger on an already running process<\/h1>\n<p>As a side-note, getting familiar with the debugger is quite useful, as one can use this on production systems to inspect an already running Java daemon. From the <a href=\"https:\/\/docs.oracle.com\/javase\/7\/docs\/technotes\/guides\/jpda\/conninv.html\">JPDA Connection and Invocation<\/a> documentation one can track down a number of Java debugger connector processes. The useful one for an already running process is the <i>SA PID Attaching Connector<\/i> run via <tt>jdb -connect sun.jvm.hotspot.jdi.SAPIDAttachingConnector:pid=&lt;pid&gt;<\/tt>.<\/p>\n<p>Similarly, today I often take <tt><a href=\"https:\/\/docs.oracle.com\/javase\/7\/docs\/technotes\/tools\/share\/jmap.html\">jmap<\/a> -dump:format=b,file=&lt;filename&gt;<\/tt> dumps of misbehaving Java processes for later analysis with <tt><a href=\"https:\/\/docs.oracle.com\/javase\/7\/docs\/technotes\/tools\/share\/jhat.html\">jhat<\/a><\/tt> but figure in the future I should perhaps investigate using <tt>sun.jvm.hotspot.jdi.SACoreAttachingConnector<\/tt> on core files of the misbehaving process to get a different view of the world.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is likely an obvious process for those who use IDE&#8217;s and develop in Maven daily but for those who do operations or otherwise need to work on the JUnit tests in HBase only infrequently, here&#8217;s how I worked when submitting a patch for HBASE-16700. First, create your code: Here I was adding a MasterObserver [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[19,24],"tags":[],"_links":{"self":[{"href":"https:\/\/clayb.net\/blog\/wp-json\/wp\/v2\/posts\/90"}],"collection":[{"href":"https:\/\/clayb.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/clayb.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/clayb.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/clayb.net\/blog\/wp-json\/wp\/v2\/comments?post=90"}],"version-history":[{"count":0,"href":"https:\/\/clayb.net\/blog\/wp-json\/wp\/v2\/posts\/90\/revisions"}],"wp:attachment":[{"href":"https:\/\/clayb.net\/blog\/wp-json\/wp\/v2\/media?parent=90"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/clayb.net\/blog\/wp-json\/wp\/v2\/categories?post=90"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/clayb.net\/blog\/wp-json\/wp\/v2\/tags?post=90"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}