Skip to main content
Glama
carloshpdoc

memorydetective

Record a Time Profiler trace

recordTimeProfile

Capture a time profile trace from an iOS app on a device or simulator. Specify the target app, duration, and output path for performance analysis.

Instructions

[mg.trace] Wrapper around xcrun xctrace record. Capture a .trace bundle from a running app on a device or simulator. Required: exactly 1 of deviceId/simulatorId, exactly 1 of attachAppName/attachPid/launchBundleId, an output path ending in .trace. Defaults: template = "Time Profiler", durationSec = 90.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
templateNoxctrace template name (e.g. "Time Profiler", "Animation Hitches", "Allocations"). Default "Time Profiler".Time Profiler
deviceIdNoUDID of a physical device. Mutually exclusive with `simulatorId`.
simulatorIdNoUDID of a simulator. Mutually exclusive with `deviceId`. Use `listTraceDevices` to find UDIDs.
attachAppNameNoAttach to a running app by name (e.g. "DemoApp"). Mutually exclusive with `attachPid` and `launchBundleId`.
attachPidNoAttach by PID. Mutually exclusive with `attachAppName` and `launchBundleId`.
launchBundleIdNoLaunch app by bundle id and start recording at launch. Mutually exclusive with `attachAppName` and `attachPid`.
durationSecNoRecording duration in seconds (default 90, max 600).
outputYesAbsolute path where the resulting `.trace` bundle should be written. Must end in `.trace`.

Implementation Reference

  • Core handler function that executes `xcrun xctrace record` with the provided input parameters, validates the output directory exists, builds xctrace arguments via buildXctraceArgs, runs the command with a timeout, and returns the result.
    export async function recordTimeProfile(
      input: RecordTimeProfileInput,
    ): Promise<RecordTimeProfileResult> {
      const output = resolvePath(input.output);
      const outDir = dirname(output);
      if (!existsSync(outDir)) {
        throw new Error(`Output directory does not exist: ${outDir}`);
      }
      const args = buildXctraceArgs({ ...input, output });
      const result = await runCommand("xcrun", args, {
        // Allow 30s grace beyond the recording duration for export/finalization.
        timeoutMs: (input.durationSec + 60) * 1_000,
      });
      if (result.code !== 0) {
        throw new Error(
          `xctrace record failed (code ${result.code}): ${result.stderr || result.stdout}`,
        );
      }
      return {
        ok: true,
        command: `xcrun ${args.join(" ")}`,
        output,
        durationSec: input.durationSec,
        template: input.template,
        stderr: result.stderr || undefined,
      };
    }
  • Input schema shape (recordTimeProfileShape) defining all parameters: template, deviceId, simulatorId, attachAppName, attachPid, launchBundleId, durationSec, output. Used directly as inputSchema in registration.
    export const recordTimeProfileShape = {
      template: z
        .string()
        .default("Time Profiler")
        .describe(
          "xctrace template name (e.g. \"Time Profiler\", \"Animation Hitches\", \"Allocations\"). Default \"Time Profiler\".",
        ),
      deviceId: z
        .string()
        .optional()
        .describe("UDID of a physical device. Mutually exclusive with `simulatorId`."),
      simulatorId: z
        .string()
        .optional()
        .describe(
          "UDID of a simulator. Mutually exclusive with `deviceId`. Use `listTraceDevices` to find UDIDs.",
        ),
      attachAppName: z
        .string()
        .optional()
        .describe(
          "Attach to a running app by name (e.g. \"DemoApp\"). Mutually exclusive with `attachPid` and `launchBundleId`.",
        ),
      attachPid: z
        .number()
        .int()
        .positive()
        .optional()
        .describe("Attach by PID. Mutually exclusive with `attachAppName` and `launchBundleId`."),
      launchBundleId: z
        .string()
        .optional()
        .describe(
          "Launch app by bundle id and start recording at launch. Mutually exclusive with `attachAppName` and `attachPid`.",
        ),
      durationSec: z
        .number()
        .int()
        .positive()
        .max(600)
        .default(90)
        .describe("Recording duration in seconds (default 90, max 600)."),
      output: z
        .string()
        .min(1)
        .describe(
          "Absolute path where the resulting `.trace` bundle should be written. Must end in `.trace`.",
        ),
    } as const;
  • Full validation schema (recordTimeProfileSchema) with superRefine logic ensuring exactly one of deviceId/simulatorId, exactly one of attachAppName/attachPid/launchBundleId, and output ends with .trace.
    export const recordTimeProfileSchema = z
      .object(recordTimeProfileShape)
      .superRefine((val, ctx) => {
        const targets = [val.deviceId, val.simulatorId].filter(Boolean).length;
        if (targets !== 1) {
          ctx.addIssue({
            code: z.ZodIssueCode.custom,
            message: "Provide exactly one of `deviceId` or `simulatorId`.",
          });
        }
        const attaches = [val.attachAppName, val.attachPid, val.launchBundleId].filter(
          (v) => v !== undefined,
        ).length;
        if (attaches !== 1) {
          ctx.addIssue({
            code: z.ZodIssueCode.custom,
            message:
              "Provide exactly one of `attachAppName`, `attachPid`, or `launchBundleId`.",
          });
        }
        if (!val.output.endsWith(".trace")) {
          ctx.addIssue({
            code: z.ZodIssueCode.custom,
            path: ["output"],
            message: "`output` must end in `.trace`.",
          });
        }
      });
  • src/index.ts:281-295 (registration)
    Tool registration via server.registerTool('recordTimeProfile', ...) with title, description, inputSchema (using recordTimeProfileShape), and async handler that calls recordTimeProfile(input).
    server.registerTool(
      "recordTimeProfile",
      {
        title: "Record a Time Profiler trace",
        description:
          "[mg.trace] Wrapper around `xcrun xctrace record`. Capture a `.trace` bundle from a running app on a device or simulator. Required: exactly 1 of `deviceId`/`simulatorId`, exactly 1 of `attachAppName`/`attachPid`/`launchBundleId`, an `output` path ending in `.trace`. Defaults: template = \"Time Profiler\", durationSec = 90.",
        inputSchema: recordTimeProfileShape,
      },
      async (input) => {
        const result = await recordTimeProfile(input);
        return {
          content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
        };
      },
    );
  • Pure helper function (buildXctraceArgs) that builds the xctrace command-line arguments array from the input parameters.
    export function buildXctraceArgs(input: RecordTimeProfileInput): string[] {
      const args = ["xctrace", "record", "--template", input.template];
      if (input.deviceId) args.push("--device", input.deviceId);
      else if (input.simulatorId) args.push("--device", input.simulatorId);
      if (input.attachAppName) args.push("--attach", input.attachAppName);
      else if (input.attachPid) args.push("--attach", String(input.attachPid));
      if (input.launchBundleId) {
        args.push("--launch", "--", input.launchBundleId);
      }
      args.push("--time-limit", `${input.durationSec}s`);
      args.push("--output", resolvePath(input.output));
      return args;
    }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden. It explains the tool is a wrapper around xcrun xctrace record and lists constraints, but does not disclose potential side effects (e.g., overwriting existing .trace files, permissions required, impact on device performance). The description is moderately transparent but lacks depth for a recording tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is three concise sentences, front-loading the purpose and then listing essential constraints and defaults. Every sentence adds value, with no redundant or extraneous information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (8 parameters, mutual exclusivity, defaults, output requirement), the description covers the key usage details. It mentions the output is a .trace bundle, but does not explain post-recording behavior or error handling. The schema fills parameter details, but the description could be more complete on behavioral outcomes.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, with each parameter already described in the input schema. The description summarizes required groups and defaults, but adds no new meaning beyond what the schema provides. It repeats constraints already in param descriptions, so it does not significantly enhance understanding.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool records a .trace bundle using xcrun xctrace, specifying the verb 'capture' and the resource '.trace bundle'. It distinguishes from sibling analysis tools by focusing on recording time profiler traces with default template 'Time Profiler' and explicitly listing required parameter groups.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly states the required parameter combinations (exactly one of deviceId/simulatorId, exactly one of attachAppName/attachPid/launchBundleId) and the required output path ending in .trace, plus defaults for template and durationSec. However, it does not explicitly mention when NOT to use this tool or suggest alternative recording tools like captureMemgraph, leaving some room for ambiguity.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/carloshpdoc/memorydetective'

If you have feedback or need assistance with the MCP directory API, please join our Discord server