import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Example {
public static void main(String[] args) {
final String regex = "(<\\s*[^>]*)(href=|src=)(.?)(\\/[^\\/])";
final String string = "<div id=\"blog\"><div id=\"content\">\n"
+ " <div id=\"content\">\n\n"
+ " <div class=\"Article\" data-slug=\"/blog/execution-traces-2024\">\n"
+ " \n"
+ " <h1 class=\"small\"><a href=\"//go.dev/blog/\">The Go Blog</a></h1>\n"
+ " \n\n"
+ " <h1>More powerful Go execution traces</h1>\n"
+ " \n"
+ " <p class=\"author\">\n"
+ " Michael Knyszek<br>\n"
+ " 14 March 2024\n"
+ " </p>\n"
+ " \n"
+ " <p>The <a href=\"/pkg/runtime/trace\">runtime/trace</a> package contains a powerful tool for understanding and\n"
+ "troubleshooting Go programs.\n"
+ "The functionality within allows one to produce a trace of each goroutine’s execution over some\n"
+ "time period.\n"
+ "With the <a href=\"/pkg/cmd/trace\"><code>go tool trace</code> command</a> (or the excellent open source\n"
+ "<a href=\"https://gotraceui.dev/\" rel=\"noreferrer\" target=\"_blank\">gotraceui tool</a>), one may then visualize and explore the data within these\n"
+ "traces.</p>\n"
+ "<p>The magic of a trace is that it can easily reveal things about a program that are hard to see in\n"
+ "other ways.\n"
+ "For example, a concurrency bottleneck where lots of goroutines block on the same channel might be\n"
+ "quite difficult to see in a CPU profile, because there’s no execution to sample.\n"
+ "But in an execution trace, the <em>lack</em> of execution will show up with amazing clarity, and the stack\n"
+ "traces of blocked goroutines will quickly point at the culprit.</p>\n"
+ "<div class=\"image\">\n"
+ " <img src=\"execution-traces-2024/gotooltrace.png\" alt=\"\">\n"
+ "</div>\n"
+ "<p>Go developers are even able to instrument their own programs with <a href=\"//go.dev/pkg/runtime/trace#Task\">tasks</a>,\n"
+ "<a href=\"//go.dev/pkg/runtime/trace#WithRegion\">regions</a>, and <a href=\"//go.dev/pkg/runtime/trace#Log\">logs</a> that\n"
+ "they can use to correlate their higher-level concerns with lower-level execution details.</p>\n"
+ "<h2 id=\"issues\">Issues</h2>\n"
+ "<p>Unfortunately, the wealth of information in execution traces can often be out of reach.\n"
+ "Four big issues with traces have historically gotten in the way.</p>\n"
+ "<ul>\n"
+ "<li>Traces had high overheads.</li>\n"
+ "<li>Traces didn’t scale well, and could become too big to analyze.</li>\n"
+ "<li>It was often unclear when to start tracing to capture a specific bad behavior.</li>\n"
+ "<li>Only the most adventurous gophers could programmatically analyze traces, given the lack of a\n"
+ "public package for parsing and interpreting execution traces.</li>\n"
+ "</ul>\n"
+ "<p>If you’ve used traces in the last few years, you’ve likely been frustrated by one or more of these\n"
+ "problems.\n"
+ "But we’re excited to share that over the last two Go releases we’ve made big progress in all four\n"
+ "of these areas.</p>\n"
+ "<h2 id=\"low-overhead-tracing\">Low-overhead tracing</h2>\n"
+ "<p>Prior to Go 1.21, the run-time overhead of tracing was somewhere between 10–20% CPU for many\n"
+ "applications, which limits tracing to situational usage, rather than continuous usage like CPU\n"
+ "profiling.\n"
+ "It turned out that much of the cost of tracing came down to tracebacks.\n"
+ "Many events produced by the runtime have stack traces attached, which are invaluable to actually\n"
+ "identifying what goroutines were doing at key moments in their execution.</p>\n"
+ "<p>Thanks to work by Felix Geisendörfer and Nick Ripley on optimizing the efficiency of tracebacks,\n"
+ "the run-time CPU overhead of execution traces has been cut dramatically, down to 1–2% for many\n"
+ "applications.\n"
+ "You can read more about the work done here in <a href=\"https://blog.felixge.de/reducing-gos-execution-tracer-overhead-with-frame-pointer-unwinding/\" rel=\"noreferrer\" target=\"_blank\">Felix’s great blog\n"
+ "post</a>\n"
+ "on the topic.</p>\n"
+ "<h2 id=\"scalable-traces\">Scalable traces</h2>\n"
+ "<p>The trace format and its events were designed around relatively efficient emission, but required\n"
+ "tooling to parse and keep around the state of the entirety of a trace.\n"
+ "A few hundred MiB trace could require several GiB of RAM to analyze!</p>\n"
+ "<p>This issue is unfortunately fundamental to how traces are generated.\n"
+ "To keep run-time overheads low, all events are written to the equivalent of thread-local buffers.\n"
+ "But this means events appear out of their true order, and the burden is placed on the trace\n"
+ "tooling to figure out what really happened.</p>\n"
+ "<p>The key insight to making traces scale while keeping overheads low was to occasionally split the\n"
+ "trace being generated.\n"
+ "Each split point would behave a bit like simultaneously disabling and reenabling tracing in one\n"
+ "go.\n"
+ "All the trace data so far would represent a complete and self-contained trace, while the new trace\n"
+ "data would seamlessly pick up from where it left off.</p>\n"
+ "<p>As you might imagine, fixing this required <a href=\"//go.dev/issue/60773\">rethinking and rewriting a lot of the foundation of\n"
+ "the trace implementation</a> in the runtime.\n"
+ "We’re happy to say that the work landed in Go 1.22 and is now generally available.\n"
+ "<a href=\"//go.dev/doc/go1.22#runtime/trace\">A lot of nice improvements</a> came with the rewrite, including some\n"
+ "improvements to the <a href=\"//go.dev/doc/go1.22#trace\"><code>go tool trace</code> command</a> as well.\n"
+ "The gritty details are all in the <a href=\"https://github.com/golang/proposal/blob/master/design/60773-execution-tracer-overhaul.md\" rel=\"noreferrer\" target=\"_blank\">design\n"
+ "document</a>,\n"
+ "if you’re curious.</p>\n"
+ "<p>(Note: <code>go tool trace</code> still loads the full trace into memory, but <a href=\"//go.dev/issue/65315\">removing this\n"
+ "limitation</a> for traces produced by Go 1.22+ programs is now feasible.)</p>\n"
+ "<h2 id=\"flight-recording\">Flight recording</h2>\n"
+ "<p>Suppose you work on a web service and an RPC took a very long time.\n"
+ "You couldn’t start tracing at the point you knew the RPC was already taking a while, because the\n"
+ "root cause of the slow request already happened and wasn’t recorded.</p>\n"
+ "<p>There’s a technique that can help with this called flight recording, which you may already be\n"
+ "familiar with from other programming environments.\n"
+ "The insight with flight recording is to have tracing on continuously and always keep the most\n"
+ "recent trace data around, just in case.\n"
+ "Then, once something interesting happens, the program can just write out whatever it has!</p>\n"
+ "<p>Before traces could be split, this was pretty much a non-starter.\n"
+ "But because continuous tracing is now viable thanks to low overheads, and the fact that the runtime\n"
+ "can now split traces any time it needs, it turns out it was straightforward to implement flight\n"
+ "recording.</p>\n"
+ "<p>As a result, we’re happy to announce a flight recorder experiment, available in the\n"
+ "<a href=\"//go.dev/pkg/golang.org/x/exp/trace#FlightRecorder\">golang.org/x/exp/trace package</a>.</p>\n"
+ "<p>Please try it out!\n"
+ "Below is an example that sets up flight recording to capture a long HTTP request to get you started.</p>\n"
+ "<div class=\"code\">\n"
+ "<pre> <span class=\"comment\">// Set up the flight recorder.</span>\n"
+ " fr := trace.NewFlightRecorder()\n"
+ " fr.Start()\n\n"
+ " <span class=\"comment\">// Set up and run an HTTP server.</span>\n"
+ " var once sync.Once\n"
+ " http.HandleFunc("/my-endpoint", func(w http.ResponseWriter, r *http.Request) {\n"
+ " start := time.Now()\n\n"
+ " <span class=\"comment\">// Do the work...</span>\n"
+ " doWork(w, r)\n\n"
+ " <span class=\"comment\">// We saw a long request. Take a snapshot!</span>\n"
+ " if time.Since(start) > 300*time.Millisecond {\n"
+ " <span class=\"comment\">// Do it only once for simplicity, but you can take more than one.</span>\n"
+ " once.Do(func() {\n"
+ " <span class=\"comment\">// Grab the snapshot.</span>\n"
+ " var b bytes.Buffer\n"
+ " _, err = fr.WriteTo(&b)\n"
+ " if err != nil {\n"
+ " log.Print(err)\n"
+ " return\n"
+ " }\n"
+ " <span class=\"comment\">// Write it to a file.</span>\n"
+ " if err := os.WriteFile("trace.out", b.Bytes(), 0o755); err != nil {\n"
+ " log.Print(err)\n"
+ " return\n"
+ " }\n"
+ " })\n"
+ " }\n"
+ " })\n"
+ " log.Fatal(http.ListenAndServe(":8080", nil))\n"
+ "</pre>\n"
+ "</div>\n"
+ "<p>If you have any feedback, positive or negative, please share it to the <a href=\"//go.dev/issue/63185\">proposal\n"
+ "issue</a>!</p>\n"
+ "<h2 id=\"trace-reader-api\">Trace reader API</h2>\n"
+ "<p>Along with the trace implementation rewrite came an effort to clean up the other trace internals,\n"
+ "like <code>go tool trace</code>.\n"
+ "This spawned an attempt to create a trace reader API that was good enough to share and that could\n"
+ "make traces more accessible.</p>\n"
+ "<p>Just like the flight recorder, we’re happy to announce that we also have an experimental trace reader\n"
+ "API that we’d like to share.\n"
+ "It’s available in the <a href=\"//go.dev/pkg/golang.org/x/exp/trace#Reader\">same package as the flight recorder,\n"
+ "golang.org/x/exp/trace</a>.</p>\n"
+ "<p>We think it’s good enough to start building things on top of, so please try it out!\n"
+ "Below is an example that measures the proportion of goroutine block events that blocked to wait on\n"
+ "the network.</p>\n"
+ "<div class=\"code\">\n"
+ "<pre> <span class=\"comment\">// Start reading from STDIN.</span>\n"
+ " r, err := trace.NewReader(os.Stdin)\n"
+ " if err != nil {\n"
+ " log.Fatal(err)\n"
+ " }\n\n"
+ " var blocked int\n"
+ " var blockedOnNetwork int\n"
+ " for {\n"
+ " <span class=\"comment\">// Read the event.</span>\n"
+ " ev, err := r.ReadEvent()\n"
+ " if err == io.EOF {\n"
+ " break\n"
+ " } else if err != nil {\n"
+ " log.Fatal(err)\n"
+ " }\n\n"
+ " <span class=\"comment\">// Process it.</span>\n"
+ " if ev.Kind() == trace.EventStateTransition {\n"
+ " st := ev.StateTransition()\n"
+ " if st.Resource.Kind == trace.ResourceGoroutine {\n"
+ " from, to := st.Goroutine()\n\n"
+ " <span class=\"comment\">// Look for goroutines blocking, and count them.</span>\n"
+ " if from.Executing() && to == trace.GoWaiting {\n"
+ " blocked++\n"
+ " if strings.Contains(st.Reason, "network") {\n"
+ " blockedOnNetwork++\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ " <span class=\"comment\">// Print what we found.</span>\n"
+ " p := 100 * float64(blockedOnNetwork) / float64(blocked)\n"
+ " fmt.Printf("%2.3f%% instances of goroutines blocking were to block on the network\\n", p)\n"
+ "</pre>\n"
+ "</div>\n"
+ "<p>And just like the flight recorder, there’s a <a href=\"//go.dev/issue/62627\">proposal issue</a> that would\n"
+ "be a great place to leave feedback!</p>\n"
+ "<p>We’d like to quickly call out Dominik Honnef as someone who tried it out early, provided great\n"
+ "feedback, and has contributed support for older trace versions to the API.</p>\n"
+ "<h2 id=\"thank-you\">Thank you!</h2>\n"
+ "<p>This work was completed, in no small part, thanks to the help of the those in the <a href=\"//go.dev/issue/57175\">diagnostics\n"
+ "working group</a>, started over a year ago as a collaboration between stakeholders from\n"
+ "across the Go community, and open to the public.</p>\n"
+ "<p>We’d like to take a moment to thank those community members who have attended the diagnostic\n"
+ "meetings regularly over the last year: Felix Geisendörfer, Nick Ripley, Rhys Hiltner, Dominik\n"
+ "Honnef, Bryan Boreham, thepudds.</p>\n"
+ "<p>The discussions, feedback, and work you all put in have been instrumental to getting us to where we\n"
+ "are today.\n"
+ "Thank you!</p>\n\n"
+ " </div>\n\n"
+ " \n"
+ " <div class=\"Article prevnext\">\n"
+ " \n"
+ " \n"
+ " \n"
+ " <p>\n"
+ " \n"
+ " \n"
+ " \n"
+ " <b>Previous article: </b><a href=\"//go.dev/blog/generic-slice-functions\">Robust generic functions on slices</a><br>\n"
+ " \n"
+ " \n"
+ " <b><a href=\"//go.dev/blog/all\">Blog Index</a></b>\n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " </div>\n"
+ " \n\n"
+ " </div>\n"
+ "</div>\n\n"
+ "<script src=\"/js/jquery.js\"></script>\n"
+ "<script src=\"/js/playground.js\"></script>\n"
+ "<script src=\"/js/play.js\"></script>\n"
+ "<script src=\"/js/godocs.js\"></script>";
final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
final Matcher matcher = pattern.matcher(string);
while (matcher.find()) {
System.out.println("Full match: " + matcher.group(0));
for (int i = 1; i <= matcher.groupCount(); i++) {
System.out.println("Group " + i + ": " + matcher.group(i));
}
}
}
}
Please keep in mind that these code samples are automatically generated and are not guaranteed to work. If you find any syntax errors, feel free to submit a bug report. For a full regex reference for Java, please visit: https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html