@Override
  protected void doGet(final HttpServletRequest req, final HttpServletResponse resp)
    throws IOException {
    if (!HttpServer.isInstrumentationAccessAllowed(getServletContext(), req, resp)) {
      resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
      setResponseHeader(resp);
      resp.getWriter().write("Unauthorized: Instrumentation access is not allowed!");
      return;
    }

    // make sure async profiler home is set
    if (asyncProfilerHome == null || asyncProfilerHome.trim().isEmpty()) {
      resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
      setResponseHeader(resp);
      resp.getWriter()
        .write("ASYNC_PROFILER_HOME env is not set.\n\n"
          + "Please ensure the prerequsites for the Profiler Servlet have been installed and the\n"
          + "environment is properly configured. For more information please see\n"
          + "http://hbase.apache.org/book.html#profiler\n");
      return;
    }

    // if pid is explicitly specified, use it else default to current process
    pid = getInteger(req, "pid", pid);

    // if pid is not specified in query param and if current process pid cannot be determined
    if (pid == null) {
      resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
      setResponseHeader(resp);
      resp.getWriter()
        .write("'pid' query parameter unspecified or unable to determine PID of current process.");
      return;
    }

    final int duration = getInteger(req, "duration", DEFAULT_DURATION_SECONDS);
    final Output output = getOutput(req);
    final Event event = getEvent(req);
    final Long interval = getLong(req, "interval");
    final Integer jstackDepth = getInteger(req, "jstackdepth", null);
    final Long bufsize = getLong(req, "bufsize");
    final boolean thread = req.getParameterMap().containsKey("thread");
    final boolean simple = req.getParameterMap().containsKey("simple");
    final Integer width = getInteger(req, "width", null);
    final Integer height = getInteger(req, "height", null);
    final Double minwidth = getMinWidth(req);
    final boolean reverse = req.getParameterMap().containsKey("reverse");

    if (process == null || !process.isAlive()) {
      try {
        int lockTimeoutSecs = 3;
        if (profilerLock.tryLock(lockTimeoutSecs, TimeUnit.SECONDS)) {
          try {
            File outputFile =
              new File(OUTPUT_DIR, "async-prof-pid-" + pid + "-" + event.name().toLowerCase() + "-"
                + ID_GEN.incrementAndGet() + "." + output.name().toLowerCase());
            List<String> cmd = new ArrayList<>();
            Path profilerScriptPath = Paths.get(asyncProfilerHome, "bin", PROFILER_SCRIPT);
            if (!Files.exists(profilerScriptPath)) {
              LOG.info(
                "async-profiler script {} does not exist, fallback to use old script {}(version <= 2.9).",
                PROFILER_SCRIPT, OLD_PROFILER_SCRIPT);
              profilerScriptPath = Paths.get(asyncProfilerHome, OLD_PROFILER_SCRIPT);
            }
            cmd.add(profilerScriptPath.toString());
            cmd.add("-e");
            cmd.add(event.getInternalName());
            cmd.add("-d");
            cmd.add("" + duration);
            cmd.add("-o");
            cmd.add(output.name().toLowerCase());
            cmd.add("-f");
            cmd.add(outputFile.getAbsolutePath());
            if (interval != null) {
              cmd.add("-i");
              cmd.add(interval.toString());
            }
            if (jstackDepth != null) {
              cmd.add("-j");
              cmd.add(jstackDepth.toString());
            }
            if (bufsize != null) {
              cmd.add("-b");
              cmd.add(bufsize.toString());
            }
            if (thread) {
              cmd.add("-t");
            }
            if (simple) {
              cmd.add("-s");
            }
            if (width != null) {
              cmd.add("--width");
              cmd.add(width.toString());
            }
            if (height != null) {
              cmd.add("--height");
              cmd.add(height.toString());
            }
            if (minwidth != null) {
              cmd.add("--minwidth");
              cmd.add(minwidth.toString());
            }
            if (reverse) {
              cmd.add("--reverse");
            }
            cmd.add(pid.toString());
            process = ProcessUtils.runCmdAsync(cmd);

            // set response and set refresh header to output location
            setResponseHeader(resp);
            resp.setStatus(HttpServletResponse.SC_ACCEPTED);
            String relativeUrl = "/prof-output-hbase/" + outputFile.getName();
            resp.getWriter()
              .write("Started [" + event.getInternalName()
                + "] profiling. This page will automatically redirect to " + relativeUrl + " after "
                + duration + " seconds. "
                + "If empty diagram and Linux 4.6+, see 'Basic Usage' section on the Async "
                + "Profiler Home Page, https://github.com/jvm-profiling-tools/async-profiler."
                + "\n\nCommand:\n" + Joiner.on(" ").join(cmd));

            // to avoid auto-refresh by ProfileOutputServlet, refreshDelay can be specified
            // via url param
            int refreshDelay = getInteger(req, "refreshDelay", 0);

            // instead of sending redirect, set auto-refresh so that browsers will refresh
            // with redirected url
            resp.setHeader("Refresh", (duration + refreshDelay) + ";" + relativeUrl);
            resp.getWriter().flush();
          } finally {
            profilerLock.unlock();
          }
        } else {
          setResponseHeader(resp);
          resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
          resp.getWriter()
            .write("Unable to acquire lock. Another instance of profiler might be running.");
          LOG.warn("Unable to acquire lock in " + lockTimeoutSecs
            + " seconds. Another instance of profiler might be running.");
        }
      } catch (InterruptedException e) {
        LOG.warn("Interrupted while acquiring profile lock.", e);
        resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
      }
    } else {
      setResponseHeader(resp);
      resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
      resp.getWriter().write("Another instance of profiler is already running.");
    }
  }
Servlet that runs async-profiler as web-endpoint. Following options from async-profiler can be
specified as query paramater. // -e event profiling event: cpu|alloc|lock|cache-misses etc. // -d
duration run profiling for 'duration' seconds (integer) // -i interval sampling interval in
nanoseconds (long) // -j jstackdepth maximum Java stack depth (integer) // -b bufsize frame
buffer size (long) // -t profile different threads separately // -s simple class names instead of
FQN // -o fmt[,fmt...] output format: summary|traces|flat|collapsed|svg|tree|jfr|html // --width
px SVG width pixels (integer) // --height px SVG frame height pixels (integer) // --minwidth px
skip frames smaller than px (double) // --reverse generate stack-reversed FlameGraph / Call tree
Example: - To collect 30 second CPU profile of current process (returns FlameGraph svg) curl
"http://localhost:10002/prof" - To collect 1 minute CPU profile of current process and output in
tree format (html) curl "http://localhost:10002/prof?output=tree&amp;duration=60" - To collect 30
second heap allocation profile of current process (returns FlameGraph svg) curl
"http://localhost:10002/prof?event=alloc" - To collect lock contention profile of current process
(returns FlameGraph svg) curl "http://localhost:10002/prof?event=lock" Following event types are
supported (default is 'cpu') (NOTE: not all OS'es support all events) // Perf events: // cpu //
page-faults // context-switches // cycles // instructions // cache-references // cache-misses //
branches // branch-misses // bus-cycles // L1-dcache-load-misses // LLC-load-misses //
dTLB-load-misses // mem:breakpoint // trace:tracepoint // Java events: // alloc // lock
hbase-http/src/main/java/org/apache/hadoop/hbase/http/ProfileServlet.java