Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions docs/src/dev/metal_logging.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
Metal Logging
=============

In debug builds, MLX compiles Metal kernels with ``os_log`` enabled so shader
warnings and debug messages are visible during development.

.. note::
Metal logging is only available with Metal 3.2 or higher (macOS 15 and up,
iOS 18 and up).

To enable logging from kernels, first make sure to build in debug mode:

.. code-block:: bash

DEBUG=1 python -m pip install -e .

Then, in the kernel source code include MLX's logging shim and use
``mlx::os_log``:

.. code-block::

#include "mlx/backend/metal/kernels/logging.h"

constant mlx::os_log logger("mlx", "my_kernel");

kernel void my_kernel(/* ... */) {
// ...
logger.log_debug("unexpected state: idx=%u", idx);
}

When you run the program, set the Metal log level to your desired level and
forward logs to ``stderr``:

.. code-block:: bash

MTL_LOG_LEVEL=MTLLogLevelDebug MTL_LOG_TO_STDERR=1 python script.py

See the `Metal logging guide`_ for more details.

.. _`Metal logging guide`: https://developer.apple.com/documentation/metal/logging-shader-debug-messages
1 change: 1 addition & 0 deletions docs/src/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -89,5 +89,6 @@ are the CPU and GPU.

dev/extensions
dev/metal_debugger
dev/metal_logging
dev/custom_metal_kernels
dev/mlx_in_cpp
2 changes: 1 addition & 1 deletion mlx/backend/metal/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function(make_jit_source SRC_FILE)
endfunction(make_jit_source)

make_jit_source(utils kernels/bf16.h kernels/bf16_math.h kernels/complex.h
kernels/defines.h)
kernels/defines.h kernels/logging.h)
make_jit_source(unary_ops kernels/erf.h kernels/expm1f.h kernels/fp8.h)
make_jit_source(binary_ops)
make_jit_source(ternary_ops)
Expand Down
7 changes: 6 additions & 1 deletion mlx/backend/metal/device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ MTL::Library* load_library(
std::ostringstream msg;
msg << "Failed to load the metallib " << lib_name << ".metallib. "
<< "We attempted to load it from <" << current_binary_dir() << "/"
<< lib_name << ".metallib" << ">";
<< lib_name << ".metallib>";
#ifdef SWIFTPM_BUNDLE
msg << " and from the Swift PM bundle.";
#endif
Expand Down Expand Up @@ -529,6 +529,11 @@ MTL::Library* Device::build_library_(const std::string& source_string) {
auto options = MTL::CompileOptions::alloc()->init();
options->setFastMathEnabled(false);
options->setLanguageVersion(get_metal_version());
#ifndef NDEBUG
if (options->languageVersion() >= MTL::LanguageVersion3_2) {
options->setEnableLogging(true);
}
#endif
auto mtl_lib = device_->newLibrary(ns_code, options, &error);
options->release();

Expand Down
4 changes: 4 additions & 0 deletions mlx/backend/metal/kernels/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ set(BASE_HEADERS
erf.h
expm1f.h
fp8.h
logging.h
utils.h)

function(build_kernel_base TARGET SRCFILE DEPS)
Expand All @@ -20,6 +21,9 @@ function(build_kernel_base TARGET SRCFILE DEPS)
if(MLX_METAL_DEBUG)
set(METAL_FLAGS ${METAL_FLAGS} -gline-tables-only -frecord-sources)
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Debug" AND MLX_METAL_VERSION GREATER_EQUAL 320)
set(METAL_FLAGS ${METAL_FLAGS} -fmetal-enable-logging)
endif()
if(NOT CMAKE_OSX_DEPLOYMENT_TARGET STREQUAL "")
set(METAL_FLAGS ${METAL_FLAGS}
"-mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}")
Expand Down
4 changes: 4 additions & 0 deletions mlx/backend/metal/kernels/binary_ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include <metal_integer>
#include <metal_math>

constant mlx::os_log logger("mlx", "binary_ops");

struct Add {
template <typename T>
T operator()(T x, T y) {
Expand Down Expand Up @@ -225,6 +227,8 @@ struct Power {
T res = 1;
// Undefined to raise integer to negative power
if (exp < 0) {
logger.log_debug(
"int pow exp<0 (base=%ld exp=%ld)", (long)base, (long)exp);
return 0;
}

Expand Down
3 changes: 3 additions & 0 deletions mlx/backend/metal/kernels/indexing/masked_scatter.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#pragma once

constant mlx::os_log logger("mlx", "masked_assign");

template <typename T, bool src_contiguous>
[[kernel]] void masked_assign_impl(
const device bool* mask [[buffer(0)]],
Expand All @@ -21,6 +23,7 @@ template <typename T, bool src_contiguous>

const uint src_index = scatter_offsets[idx];
if (src_index >= src_batch_size) {
logger.log_debug("Out of bound read from src");
return;
}

Expand Down
26 changes: 26 additions & 0 deletions mlx/backend/metal/kernels/logging.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright © 2025 Apple Inc.

#pragma once

#if defined(__METAL_VERSION__) && (__METAL_VERSION__ >= 320)
#include <metal_logging>

namespace mlx {
using os_log = metal::os_log;
} // namespace mlx

#else

namespace mlx {
struct os_log {
constexpr os_log(constant char*, constant char*) constant {}

template <typename... Args>
void log_debug(constant char*, Args...) const {}

template <typename... Args>
void log_debug(constant char*, Args...) const constant {}
};
} // namespace mlx

#endif
1 change: 1 addition & 0 deletions mlx/backend/metal/kernels/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "mlx/backend/metal/kernels/bf16_math.h"
#include "mlx/backend/metal/kernels/complex.h"
#include "mlx/backend/metal/kernels/defines.h"
#include "mlx/backend/metal/kernels/logging.h"

typedef half float16_t;

Expand Down
Loading