More robust FreeBSD topology detection (#249)

Possibly fix #248 
diff --git a/README.md b/README.md
index 82cadea..ec05f53 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
 ## Features
 
 - **Cross-platform** availability:
-  - Linux, Windows, macOS, Android, and iOS operating systems
+  - Linux, Windows, macOS, Android, iOS and FreeBSD operating systems
   - x86, x86-64, ARM, and ARM64 architectures
 - Modern **C/C++ interface**
   - Thread-safe
@@ -258,6 +258,8 @@
   - [x] x86
   - [x] x86-64
   - [x] arm64
+- [x] FreeBSD
+  - [x] x86-64
 
 ## Methods
 
diff --git a/src/freebsd/topology.c b/src/freebsd/topology.c
index da941e9..675a81f 100644
--- a/src/freebsd/topology.c
+++ b/src/freebsd/topology.c
@@ -24,8 +24,10 @@
 	size_t value_size = 0;
 	if (sysctlbyname(name, NULL, &value_size, NULL, 0) != 0) {
 		cpuinfo_log_error("sysctlbyname(\"%s\") failed: %s", name, strerror(errno));
+		return NULL;
 	} else if (value_size <= 0) {
 		cpuinfo_log_error("sysctlbyname(\"%s\") returned invalid value size %zu", name, value_size);
+		return NULL;
 	}
 	value_size += 1;
 	char* value = calloc(value_size, 1);
@@ -52,29 +54,22 @@
 	if (!topology_spec) {
 		return topology;
 	}
-	const char* group_tag = "<group level=\"1\" cache-level=\"0\">";
-	char* p = strstr(topology_spec, group_tag);
-	while (p) {
-		const char* cpu_tag = "cpu count=\"";
-		char* q = strstr(p, cpu_tag);
-		if (q) {
-			p = q + strlen(cpu_tag);
-			topology.packages += atoi(p);
-		} else {
-			break;
-		}
-	}
-	if (topology.packages == 0) {
-		const char* group_tag = "<group level=\"1\"";
+	const char* group_tags[] = {"<group level=\"2\" cache-level=\"0\">", "<group level=\"1\" "};
+	for (size_t i = 0; i < sizeof(group_tags) / sizeof(group_tags[0]); i++) {
+		const char* group_tag = group_tags[i];
 		char* p = strstr(topology_spec, group_tag);
 		while (p) {
 			topology.packages += 1;
 			p++;
 			p = strstr(p, group_tag);
 		}
+		if (topology.packages > 0) {
+			break;
+		}
 	}
+
 	if (topology.packages == 0) {
-		cpuinfo_log_error("failed to parse topology_spec:%s", topology_spec);
+		cpuinfo_log_error("failed to parse topology_spec: %s", topology_spec);
 		free(topology_spec);
 		goto fail;
 	}
@@ -84,6 +79,7 @@
 		goto fail;
 	}
 	if (topology.cores < topology.packages) {
+		cpuinfo_log_error("invalid numbers of package and core: %d %d", topology.packages, topology.cores);
 		goto fail;
 	}
 	topology.threads_per_core = sysctl_int("kern.smp.threads_per_core");
diff --git a/src/x86/freebsd/init.c b/src/x86/freebsd/init.c
index c6c6d75..797fa24 100644
--- a/src/x86/freebsd/init.c
+++ b/src/x86/freebsd/init.c
@@ -135,6 +135,10 @@
 	if (x86_processor.cache.l1i.size != 0 || x86_processor.cache.l1d.size != 0) {
 		/* Assume that threads on the same core share L1 */
 		threads_per_l1 = freebsd_topology.threads / freebsd_topology.cores;
+		if (threads_per_l1 == 0) {
+			cpuinfo_log_error("failed to detect threads_per_l1");
+			goto cleanup;
+		}
 		cpuinfo_log_warning(
 			"freebsd kernel did not report number of "
 			"threads sharing L1 cache; assume %" PRIu32,
@@ -154,6 +158,10 @@
 			 * the same package share L2 */
 			threads_per_l2 = freebsd_topology.threads / freebsd_topology.packages;
 		}
+		if (threads_per_l2 == 0) {
+			cpuinfo_log_error("failed to detect threads_per_l1");
+			goto cleanup;
+		}
 		cpuinfo_log_warning(
 			"freebsd kernel did not report number of "
 			"threads sharing L2 cache; assume %" PRIu32,
@@ -170,6 +178,10 @@
 		 * may be L4 cache as well)
 		 */
 		threads_per_l3 = freebsd_topology.threads / freebsd_topology.packages;
+		if (threads_per_l3 == 0) {
+			cpuinfo_log_error("failed to detect threads_per_l3");
+			goto cleanup;
+		}
 		cpuinfo_log_warning(
 			"freebsd kernel did not report number of "
 			"threads sharing L3 cache; assume %" PRIu32,
@@ -187,6 +199,10 @@
 		 * shared L4 (like on IBM POWER8).
 		 */
 		threads_per_l4 = freebsd_topology.threads;
+		if (threads_per_l4 == 0) {
+			cpuinfo_log_error("failed to detect threads_per_l4");
+			goto cleanup;
+		}
 		cpuinfo_log_warning(
 			"freebsd kernel did not report number of "
 			"threads sharing L4 cache; assume %" PRIu32,
@@ -203,7 +219,7 @@
 				"%" PRIu32 " L1I caches",
 				l1_count * sizeof(struct cpuinfo_cache),
 				l1_count);
-			return;
+			goto cleanup;
 		}
 		for (uint32_t c = 0; c < l1_count; c++) {
 			l1i[c] = (struct cpuinfo_cache){
@@ -230,7 +246,7 @@
 				"%" PRIu32 " L1D caches",
 				l1_count * sizeof(struct cpuinfo_cache),
 				l1_count);
-			return;
+			goto cleanup;
 		}
 		for (uint32_t c = 0; c < l1_count; c++) {
 			l1d[c] = (struct cpuinfo_cache){
@@ -257,7 +273,7 @@
 				"%" PRIu32 " L2 caches",
 				l2_count * sizeof(struct cpuinfo_cache),
 				l2_count);
-			return;
+			goto cleanup;
 		}
 		for (uint32_t c = 0; c < l2_count; c++) {
 			l2[c] = (struct cpuinfo_cache){
@@ -284,7 +300,7 @@
 				"%" PRIu32 " L3 caches",
 				l3_count * sizeof(struct cpuinfo_cache),
 				l3_count);
-			return;
+			goto cleanup;
 		}
 		for (uint32_t c = 0; c < l3_count; c++) {
 			l3[c] = (struct cpuinfo_cache){
@@ -311,7 +327,7 @@
 				"%" PRIu32 " L4 caches",
 				l4_count * sizeof(struct cpuinfo_cache),
 				l4_count);
-			return;
+			goto cleanup;
 		}
 		for (uint32_t c = 0; c < l4_count; c++) {
 			l4[c] = (struct cpuinfo_cache){