@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.");
      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");
    final String method = req.getParameter("method");

    if (req.getParameter("event") != null && method != null) {
      resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
      setResponseHeader(resp);
      resp.getWriter().write("Event and method aren't allowed to be both used in the same request.");
      return;
    }

    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 + "-" +
              (method == null ? event.name().toLowerCase() : method) + "-" + ID_GEN.incrementAndGet() + "." +
              output.name().toLowerCase());
            List<String> cmd = new ArrayList<>();
            cmd.add(getProfilerScriptPath());
            cmd.add("-e");
            cmd.add(method == null ? event.getInternalName() : method);
            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/" + outputFile.getName();
            resp.getWriter().write(
              "Started [" + event.getInternalName() + "] profiling. This page will automatically redirect to " +
                relativeUrl + " after " + duration + " seconds.\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 {} seconds. Another instance of profiler might be running.",
            lockTimeoutSecs);
        }
      } 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 parameter.
//  -e event          profiling event: cpu|alloc|lock|cache-misses etc.
//  -d duration       run profiling for &lt;duration&gt; seconds (integer)
//  -i interval       sampling interval in nanoseconds (long)
//  -j jstackdepth    maximum Java stack depth (integer)
//  -b bufsize        frame buffer size (long)
//  -m method         fully qualified method name: 'ClassName.methodName'
//  -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 10 second CPU profile of current process (returns FlameGraph html)
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 10 second heap allocation profile of current process (returns FlameGraph html)
curl "http://localhost:10002/prof?event=alloc"
- To collect lock contention profile of current process (returns FlameGraph html)
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
common/src/java/org/apache/hive/http/ProfileServlet.java