smiklosovic commented on code in PR #4487: URL: https://github.com/apache/cassandra/pull/4487#discussion_r2556131335
########## src/java/org/apache/cassandra/tools/profiler/AsyncProfilerService.java: ########## @@ -0,0 +1,166 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.cassandra.tools.profiler; + +import one.profiler.AsyncProfiler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Set; +import java.util.regex.Pattern; + +import static org.apache.cassandra.config.CassandraRelevantProperties.ASYNC_PROFILER_ENABLED; +import static org.apache.cassandra.config.CassandraRelevantProperties.ASYNC_PROFILER_OUTPUT_DIRECTORY; + +public class AsyncProfilerService +{ + private static final Logger logger = LoggerFactory.getLogger(AsyncProfilerService.class); + + private static final Set<String> VALID_EVENTS = Set.of("cpu", "alloc", "lock", "wall", "nativemem", "cache-misses"); + private static final Set<String> VALID_FORMATS = Set.of("flat","traces","collapsed","flamegraph","tree","jfr","otlp"); + + private static final Pattern VALID_FILENAME_REGEX_PATTERN = Pattern.compile("^[a-zA-Z0-9-]*\\.?[a-zA-Z0-9-]*$"); + + private static AsyncProfiler profilerInstance; + + static + { + try + { + // Let async-profiler automatically extract and load the native library from the JAR + profilerInstance = AsyncProfiler.getInstance(); + } + catch (Throwable t) + { + logger.error("async-profiler initialization ERROR"); + profilerInstance = null; + } + } + + public void start(String event, String outputFormat, int timeout, String outputFileName) + { + checkProfilerInstance(); + validateEvent(event); + validateFormat(outputFormat); + validateOutputFileName(outputFileName); + String outputPath = Path.of(ASYNC_PROFILER_OUTPUT_DIRECTORY.getString(), outputFileName).toString(); + try + { + String cmd = String.format("start,%s,event=%s,timeout=%s,file=%s", + outputFormat, + event, + timeout, + outputPath); + + profilerInstance.execute(cmd); + logger.info("Started async-profiler: cmd={}", cmd); + } + catch (IOException e) + { + logger.error("Failed to start async-profiler", e); + throw new RuntimeException(e); + } + } + + public void stop(String outputFileName) + { + checkProfilerInstance(); + validateOutputFileName(outputFileName); + + String outputPath = Path.of(ASYNC_PROFILER_OUTPUT_DIRECTORY.getString(), outputFileName).toString(); + + String cmd = String.format("stop,file=%s", + outputPath); + + try + { + profilerInstance.execute(cmd); + logger.info("Stopped async-profiler: cmd={}", cmd); + } catch (IOException e) + { + logger.error("Failed to stop async-profiler", e); + throw new RuntimeException(e); + } + } + + public void execute(String command) + { + checkProfilerInstance(); + + try + { + profilerInstance.execute(command); + logger.info("Executed raw async-profiler command {}", command); + } + catch (IOException e) + { + logger.error("Failed to execute raw async-profiler command {}", command, e); + throw new RuntimeException(e); + } + } + + public boolean isAvailable() + { + return profilerInstance != null; + } + + private void checkProfilerInstance() + { + if (ASYNC_PROFILER_ENABLED.getBoolean() == false) + { + throw new IllegalStateException("async-profiler is not enabled."); + } + else if (!isAvailable()) + { + throw new IllegalStateException("async-profiler is not initialized."); + } + } + + private void validateEvent(String event) Review Comment: These validation methods might be replaced by a static method on each respective logical enum which you can reuse in nodetool. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]

