Skip to content

Commit 31eaf61

Browse files
authored
Merge pull request #1254 from microsoft/littleaj/perfcounterlogging
Improve logging/exception handling loading JNI perf counter lib
2 parents 2319268 + 62893c6 commit 31eaf61

File tree

5 files changed

+76
-18
lines changed

5 files changed

+76
-18
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# CHANGELOG
22

3+
# Version 2.6.2
4+
* Added additional error logging for troubleshooting issues loading JNI performance counter library.
5+
36
# Version 2.6.1
47
* Fix shutdown issue due to non-daemon thread
58
([#1245](https://github.com/microsoft/ApplicationInsights-Java/pull/1245))
@@ -359,4 +362,3 @@ Livemetrics UX.
359362
- Support collecting performance counters from 32-bit Windows machines.
360363
- Support manual tracking of dependencies using a new ```trackDependency``` method API.
361364
- Ability to tag a telemetry item as synthetic, by adding a ```SyntheticSource``` property to the reported item.
362-

core/native.gradle

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
*/
2121

2222
apply plugin: 'cpp'
23+
apply plugin: 'idea'
2324

2425
buildscript {
2526
repositories {
@@ -46,6 +47,12 @@ if (System.env.APPINSIGHTS_VS_PATH) {
4647
println "Using custom Visual Studio Tools path: $vsToolsDir"
4748
}
4849

50+
idea {
51+
module {
52+
sourceDirs += file("$projectDir/src/windows/cpp")
53+
}
54+
}
55+
4956
// region Native binary definition and configuration
5057
model {
5158
toolChains {

core/src/main/java/com/microsoft/applicationinsights/internal/perfcounter/JniPCConnector.java

Lines changed: 59 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.apache.commons.io.FileUtils;
3030
import org.apache.commons.io.IOUtils;
3131
import org.apache.commons.lang3.StringUtils;
32+
import org.apache.commons.lang3.exception.ExceptionUtils;
3233

3334
import java.io.File;
3435
import java.io.IOException;
@@ -75,11 +76,15 @@ public static boolean initialize() {
7576
loadNativeLibrary();
7677
} catch (ThreadDeath td) {
7778
throw td;
79+
} catch (JNIPerformanceCounterConnectorException e) {
80+
if (InternalLogger.INSTANCE.isErrorEnabled()) {
81+
InternalLogger.INSTANCE.error("Error initializing JNI Performance Counter library. Windows performance counters will not be used.: "+ ExceptionUtils.getStackTrace(e));
82+
}
7883
} catch (Throwable e) {
7984
try {
80-
InternalLogger.INSTANCE.error(
81-
"Failed to load native dll, Windows performance counters will not be used. " +
82-
"Please make sure that Visual C++ Redistributable is properly installed: %s.", e.toString());
85+
if (InternalLogger.INSTANCE.isErrorEnabled()) {
86+
InternalLogger.INSTANCE.error("Unexpected error initializing JNI Performance Counter library. Windows performance counters will not be used: "+ExceptionUtils.getStackTrace(e));
87+
}
8388

8489
return false;
8590
} catch (ThreadDeath td) {
@@ -150,14 +155,26 @@ public static double getValueOfPerformanceCounter(String name) {
150155
return getPerformanceCounterValue(name);
151156
}
152157

158+
/**
159+
* Performance Counters identify a process by "Instance Name."
160+
* This will be the executable name without the extension, e.g. a process running java.exe will have an instance name "java".
161+
* If there are multiple instances of the same executable, an additional identifier is appended. By default this looks like "java#1", "java#2".
162+
* For some reason, the instance name can change after the process starts with the default naming scheme.
163+
*
164+
* To workaround this, add a DWORD registry value named 'ProcessNameFormat' set to the value '2' to the key
165+
* 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\PerfProc\Performance'.
166+
* This changes the naming scheme from "java#2" to "java_PID" where PID is the current process id. It also makes the name constant for the life of the process.
167+
*
168+
* @throws NumberFormatException if pid cannot be parsed.
169+
*/
153170
private static void initNativeCode() {
154171
int processId = Integer.parseInt(SystemInformation.INSTANCE.getProcessId());
155172

156173
currentInstanceName = getInstanceName(processId);
157174
if (StringUtils.isEmpty(currentInstanceName)) {
158175
InternalLogger.INSTANCE.error("Failed to fetch current process instance name, process counters for for the process level will not be activated.");
159176
} else {
160-
InternalLogger.INSTANCE.trace("Java process name is set to '%s'", currentInstanceName);
177+
InternalLogger.INSTANCE.info("Java process instance name is set to '%s'", currentInstanceName);
161178
}
162179
}
163180

@@ -173,21 +190,39 @@ private static void initNativeCode() {
173190
* @throws IOException If there are errors in opening/writing/reading/closing etc.
174191
* Note that the method might throw RuntimeExceptions due to critical issues
175192
*/
176-
private static void loadNativeLibrary() throws IOException {
177-
String model = System.getProperty("sun.arch.data.model");
178-
String libraryToLoad = BITS_MODEL_64.equals(model) ? NATIVE_LIBRARY_64 : NATIVE_LIBRARY_32;
193+
private static void loadNativeLibrary() throws JNIPerformanceCounterConnectorException {
194+
final File dllOnDisk;
195+
final String libraryToLoad;
196+
try {
197+
String model = System.getProperty("sun.arch.data.model");
198+
libraryToLoad = BITS_MODEL_64.equals(model) ? NATIVE_LIBRARY_64 : NATIVE_LIBRARY_32;
179199

180-
File dllPath = buildDllLocalPath();
200+
File dllPath = buildDllLocalPath();
181201

182-
File dllOnDisk = new File(dllPath, libraryToLoad);
202+
dllOnDisk = new File(dllPath, libraryToLoad);
183203

184-
if (!dllOnDisk.exists()) {
185-
extractToLocalFolder(dllOnDisk, libraryToLoad);
204+
if (!dllOnDisk.exists()) {
205+
extractToLocalFolder(dllOnDisk, libraryToLoad);
206+
} else if (InternalLogger.INSTANCE.isTraceEnabled()) {
207+
InternalLogger.INSTANCE.trace("Found existing DLL: " + dllOnDisk.getAbsolutePath());
208+
}
209+
} catch (Exception e) {
210+
throw new JNIPerformanceCounterConnectorException("Error extracting DLL to disk", e);
186211
}
187212

188-
System.load(dllOnDisk.toString());
213+
try {
214+
System.load(dllOnDisk.toString());
215+
} catch (Exception e) {
216+
throw new JNIPerformanceCounterConnectorException("Error loading DLL. Please make sure that Visual C++ 2015 Redistributable is properly installed", e);
217+
}
189218

190-
initNativeCode();
219+
try {
220+
initNativeCode();
221+
} catch (NumberFormatException e) {
222+
throw new JNIPerformanceCounterConnectorException("Could not parse PID as int", e);
223+
} catch (Exception e) {
224+
throw new JNIPerformanceCounterConnectorException("Unexpected error initializing performance counter DLL library", e);
225+
}
191226

192227
InternalLogger.INSTANCE.trace("Successfully loaded library '%s'", libraryToLoad);
193228
}
@@ -243,4 +278,14 @@ private static File buildDllLocalPath() {
243278

244279
return dllPath;
245280
}
246-
}
281+
282+
private static class JNIPerformanceCounterConnectorException extends Exception {
283+
public JNIPerformanceCounterConnectorException(String s) {
284+
super(s);
285+
}
286+
287+
public JNIPerformanceCounterConnectorException(String s, Throwable throwable) {
288+
super(s, throwable);
289+
}
290+
}
291+
}

core/src/main/java/com/microsoft/applicationinsights/internal/system/SystemInformation.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ public boolean isUnix() {
5050
return SystemUtils.IS_OS_UNIX;
5151
}
5252

53+
/**
54+
* JVMs are not required to publish this value/bean and some processes may not have permission to access it.
55+
*/
5356
private String initializeProcessId() {
5457
String rawName = ManagementFactory.getRuntimeMXBean().getName();
5558
if (!Strings.isNullOrEmpty(rawName)) {
@@ -58,13 +61,14 @@ private String initializeProcessId() {
5861
String processIdAsString = rawName.substring(0, i);
5962
try {
6063
Integer.parseInt(processIdAsString);
64+
InternalLogger.INSTANCE.info("Current PID: "+processIdAsString);
6165
return processIdAsString;
6266
} catch (Exception e) {
63-
InternalLogger.INSTANCE.error("Failed to fetch process id: '%s'", e.toString());
67+
InternalLogger.INSTANCE.error("Failed to parse PID as number: '%s'", e.toString());
6468
}
6569
}
6670
}
67-
71+
InternalLogger.INSTANCE.error("Could not extract PID from runtime name: '"+rawName+"'");
6872
// Default
6973
return DEFAULT_PROCESS_NAME;
7074
}

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
// Project properties
2-
version=2.6.1
2+
version=2.6.2
33
group=com.microsoft.azure
44

0 commit comments

Comments
 (0)