static int fcn_print_json(RzCore *core, RzAnalysisFunction *fcn, PJ *pj) {
	RzListIter *iter;
	RzAnalysisXRef *xrefi;
	if (!pj) {
		return -1;
	}
	int ebbs = 0;
	pj_o(pj);
	pj_kn(pj, "offset", fcn->addr);
	char *name = rz_core_analysis_fcn_name(core, fcn);
	if (name) {
		pj_ks(pj, "name", name);
	}
	pj_kn(pj, "size", rz_analysis_function_linear_size(fcn));
	pj_ks(pj, "is-pure", rz_str_bool(rz_analysis_function_purity(fcn)));
	pj_kn(pj, "realsz", rz_analysis_function_realsize(fcn));
	pj_kb(pj, "noreturn", fcn->is_noreturn);
	pj_ki(pj, "stackframe", fcn->maxstack);
	if (fcn->cc) {
		pj_ks(pj, "calltype", fcn->cc); // calling conventions
	}
	pj_ki(pj, "cost", rz_analysis_function_cost(fcn)); // execution cost
	pj_ki(pj, "cc", rz_analysis_function_complexity(fcn)); // cyclic cost
	pj_ki(pj, "bits", fcn->bits);
	pj_ks(pj, "type", rz_analysis_fcntype_tostring(fcn->type));
	pj_ki(pj, "nbbs", rz_list_length(fcn->bbs));
	pj_ki(pj, "edges", rz_analysis_function_count_edges(fcn, &ebbs));
	pj_ki(pj, "ebbs", ebbs);
	{
		char *sig = rz_core_analysis_function_signature(core, RZ_OUTPUT_MODE_STANDARD, fcn->name);
		if (sig) {
			rz_str_trim(sig);
			pj_ks(pj, "signature", sig);
			free(sig);
		}
	}
	pj_kn(pj, "minbound", rz_analysis_function_min_addr(fcn));
	pj_kn(pj, "maxbound", rz_analysis_function_max_addr(fcn));

	int outdegree = 0;
	RzList *xrefs = rz_analysis_function_get_xrefs_from(fcn);
	if (!rz_list_empty(xrefs)) {
		pj_k(pj, "callrefs");
		pj_a(pj);
		rz_list_foreach (xrefs, iter, xrefi) {
			if (xrefi->type == RZ_ANALYSIS_REF_TYPE_CALL) {
				outdegree++;
			}
			if (xrefi->type == RZ_ANALYSIS_REF_TYPE_CODE ||
				xrefi->type == RZ_ANALYSIS_REF_TYPE_CALL) {
				pj_o(pj);
				pj_kn(pj, "from", xrefi->from);
				pj_kn(pj, "to", xrefi->to);
				pj_ks(pj, "type", rz_analysis_xrefs_type_tostring(xrefi->type));
				pj_end(pj);
			}
		}
		pj_end(pj);

		pj_k(pj, "datarefs");
		pj_a(pj);
		rz_list_foreach (xrefs, iter, xrefi) {
			if (xrefi->type == RZ_ANALYSIS_REF_TYPE_DATA) {
				pj_n(pj, xrefi->to);
			}
		}
		pj_end(pj);
	}
	rz_list_free(xrefs);

	int indegree = 0;
	xrefs = rz_analysis_function_get_xrefs_to(fcn);
	if (!rz_list_empty(xrefs)) {
		pj_k(pj, "codexrefs");
		pj_a(pj);
		rz_list_foreach (xrefs, iter, xrefi) {
			if (xrefi->type == RZ_ANALYSIS_REF_TYPE_CODE ||
				xrefi->type == RZ_ANALYSIS_REF_TYPE_CALL) {
				indegree++;
				pj_o(pj);
				pj_kn(pj, "from", xrefi->from);
				pj_kn(pj, "to", xrefi->to);
				pj_ks(pj, "type", rz_analysis_xrefs_type_tostring(xrefi->type));
				pj_end(pj);
			}
		}

		pj_end(pj);
		pj_k(pj, "dataxrefs");
		pj_a(pj);

		rz_list_foreach (xrefs, iter, xrefi) {
			if (xrefi->type == RZ_ANALYSIS_REF_TYPE_DATA) {
				pj_n(pj, xrefi->from);
			}
		}
		pj_end(pj);
	}
	rz_list_free(xrefs);

	pj_ki(pj, "indegree", indegree);
	pj_ki(pj, "outdegree", outdegree);

	if (fcn->type == RZ_ANALYSIS_FCN_TYPE_FCN || fcn->type == RZ_ANALYSIS_FCN_TYPE_SYM) {
		pj_ki(pj, "nlocals", rz_analysis_var_count(core->analysis, fcn, 'b', 0) + rz_analysis_var_count(core->analysis, fcn, 'r', 0) + rz_analysis_var_count(core->analysis, fcn, 's', 0));
		pj_ki(pj, "nargs", rz_analysis_var_count(core->analysis, fcn, 'b', 1) + rz_analysis_var_count(core->analysis, fcn, 'r', 1) + rz_analysis_var_count(core->analysis, fcn, 's', 1));

		pj_k(pj, "bpvars");
		rz_analysis_var_list_show(core->analysis, fcn, 'b', 'j', pj);
		pj_k(pj, "spvars");
		rz_analysis_var_list_show(core->analysis, fcn, 's', 'j', pj);
		pj_k(pj, "regvars");
		rz_analysis_var_list_show(core->analysis, fcn, 'r', 'j', pj);

		pj_ks(pj, "difftype", fcn->diff->type == RZ_ANALYSIS_DIFF_TYPE_MATCH ? "match" : fcn->diff->type == RZ_ANALYSIS_DIFF_TYPE_UNMATCH ? "unmatch"
																		  : "new");
		if (fcn->diff->addr != -1) {
			pj_kn(pj, "diffaddr", fcn->diff->addr);
		}
		if (fcn->diff->name) {
			pj_ks(pj, "diffname", fcn->diff->name);
		}
	}
	pj_end(pj);
	free(name);
	return 0;
}