utils.ts 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. import groupBy from 'lodash/groupBy';
  2. import {CallTreeNode} from 'sentry/utils/profiling/callTreeNode';
  3. import {Profile} from 'sentry/utils/profiling/profile/profile';
  4. export function collectProfileFrames(profile: Profile) {
  5. const nodes: CallTreeNode[] = [];
  6. profile.forEach(
  7. node => {
  8. if (node.selfWeight > 0) {
  9. nodes.push(node);
  10. }
  11. },
  12. () => {}
  13. );
  14. return nodes
  15. .sort((a, b) => b.selfWeight - a.selfWeight)
  16. .map(node => ({
  17. symbol: node.frame.name,
  18. file: node.frame.file,
  19. image: node.frame.image,
  20. thread: profile.threadId,
  21. type: node.frame.is_application ? 'application' : 'system',
  22. 'self weight': node.selfWeight,
  23. 'total weight': node.totalWeight,
  24. }));
  25. }
  26. export function pluckUniqueValues<T extends Record<string, any>>(
  27. collection: T[],
  28. key: keyof T
  29. ) {
  30. return collection.reduce((acc, record) => {
  31. const value = record[key];
  32. if (value && !acc.includes(value)) {
  33. acc.push(value);
  34. }
  35. return acc;
  36. }, [] as any[]);
  37. }
  38. export type Row<K extends string = string> = Record<
  39. Extract<K, string>,
  40. string | number | any[]
  41. >;
  42. export interface AggregateColumnConfig<K extends string> {
  43. compute: (data: Row<K>[]) => number | any[];
  44. key: string;
  45. }
  46. export function aggregate<T extends string>(
  47. data: Partial<Row<T>>[],
  48. groups: Extract<T, string>[],
  49. aggregates: AggregateColumnConfig<T>[]
  50. ): Row<T>[] {
  51. // group by a key composed by unique values
  52. // ex: { a: "foo", b: "bar" } => { "foo bar": [...] }
  53. const groupedData = groupBy(data, row => getGroupedKey(row, groups));
  54. const rows: Row[] = [];
  55. for (const groupedKey in groupedData) {
  56. // unwrap the grouped key into a base value
  57. // ex: { "foo bar": [...] } => {a: "foo", b: "bar"}
  58. const row = makeRowFromGroupedKey(groupedKey, groups);
  59. const groupedValues = groupedData[groupedKey] as Row<T>[];
  60. aggregates.forEach(agg => {
  61. // do the actual aggregation with the grouped values
  62. // ex: { a: "foo", b: "bar", sum: 123 }
  63. row[agg.key] = agg.compute(groupedValues);
  64. });
  65. rows.push(row);
  66. }
  67. return rows;
  68. }
  69. // we'll use the "unit separator" character to delimit grouped values
  70. // https://en.wikipedia.org/wiki/Delimiter#ASCII_delimited_text
  71. const FIELD_SEPARATOR = String.fromCharCode(31);
  72. // getGroupedKey will derive a key from an objects values and delimit them using
  73. // the unit separator
  74. function getGroupedKey(row: Record<string, unknown>, groups: string[]) {
  75. return groups.map(key => row[key]).join(FIELD_SEPARATOR);
  76. }
  77. function makeRowFromGroupedKey(groupedKey: string, groups: string[]) {
  78. const groupedKeyValues = groupedKey.split(FIELD_SEPARATOR);
  79. return groups.reduce((acc, key, idx) => {
  80. acc[key] = groupedKeyValues[idx];
  81. return acc;
  82. }, {} as Row);
  83. }