diff --git a/control/descriptor.c b/control/descriptor.c
index 62331e331b930470dd857f2b3a22d08a5a4ae68f..8d65d4cf66c151d19090b744127b0be6ba89b7a1 100644
--- a/control/descriptor.c
+++ b/control/descriptor.c
@@ -976,3 +976,48 @@ void CHAMELEON_user_tag_size(int user_tag_width, int user_tag_sep) {
     RUNTIME_comm_set_tag_sizes( user_tag_width, user_tag_sep );
     return;
 }
+
+static void
+chameleon_desc_print( const CHAM_desc_t *desc, int shift )
+{
+    intptr_t base = (intptr_t)desc->mat;
+    int m, n;
+
+    for ( n=0; n<desc->nt; n++ ) {
+        for ( m=0; m<desc->mt; m++ ) {
+            const CHAM_tile_t *tile;
+            const CHAM_desc_t *tiledesc;
+            intptr_t ptr;
+
+            tile     = desc->get_blktile( desc, m, n );
+            tiledesc = tile->mat;
+
+            ptr = ( tile->format == CHAMELEON_TILE_DESC ) ? (intptr_t)(tiledesc->mat) : (intptr_t)(tile->mat);
+
+            fprintf( stdout, "%*s%s(%3d,%3d): %d * %d / ld = %d / offset= %ld\n",
+                     shift, " ", desc->name, m, n, tile->m, tile->n, tile->ld, ptr - base );
+
+            if ( tile->format == CHAMELEON_TILE_DESC ) {
+                chameleon_desc_print( tiledesc, shift+2 );
+            }
+        }
+    }
+}
+
+/**
+ *****************************************************************************
+ *
+ * @ingroup Descriptor
+ *
+ *  @brief Print descriptor structure for debug purpose
+ *
+ ******************************************************************************
+ *
+ * @param[in] desc
+ *          The input desc for which to describe to print the tile structure
+ */
+void
+CHAMELEON_Desc_Print( const CHAM_desc_t *desc )
+{
+    chameleon_desc_print( desc, 2 );
+}
diff --git a/include/chameleon.h b/include/chameleon.h
index 4a7a72bbda201e4e94389972a69255393021e1ce..1dc06c76696437715be3e1dce47ce26e46c6ac37 100644
--- a/include/chameleon.h
+++ b/include/chameleon.h
@@ -140,6 +140,7 @@ int CHAMELEON_Desc_Acquire( const CHAM_desc_t *desc );
 int CHAMELEON_Desc_Release( const CHAM_desc_t *desc );
 int CHAMELEON_Desc_Flush  ( const CHAM_desc_t        *desc,
                             const RUNTIME_sequence_t *sequence );
+void CHAMELEON_Desc_Print( const CHAM_desc_t *desc );
 
 /* Workspaces */
 int CHAMELEON_Dealloc_Workspace (CHAM_desc_t **desc);
diff --git a/testing/CMakeLists.txt b/testing/CMakeLists.txt
index ee99eb5983a823174d044869d947d45e4b0180b7..1079e455cd9f8f17ecb33d4e5a3317efdb8c50b3 100644
--- a/testing/CMakeLists.txt
+++ b/testing/CMakeLists.txt
@@ -45,6 +45,7 @@ set(CHAMELEON_SOURCES_TARGETS "${CHAMELEON_SOURCES_TARGETS};testing_include" CAC
 set(ZSRC
   chameleon_ztesting.c
   testing_zcheck.c
+  testing_zprint.c
   ##################
   # LAPACK
   ##################
diff --git a/testing/CTestLists.cmake b/testing/CTestLists.cmake
index 007d8013938501d3e90f079eef6bb60d4740d3e2..9ca61431a485b6eb9251d208ddf5d2581b549f53 100644
--- a/testing/CTestLists.cmake
+++ b/testing/CTestLists.cmake
@@ -26,7 +26,7 @@ if (NOT CHAMELEON_SIMULATION)
     #
     # Create the list of test based on precision and runtime
     #
-    set( TESTS lacpy lange lantr lansy plrnk )
+    set( TESTS print lacpy lange lantr lansy plrnk )
     if ( ${prec} STREQUAL c OR ${prec} STREQUAL z )
       set( TESTS ${TESTS} lanhe )
     endif()
diff --git a/testing/input/print.in b/testing/input/print.in
new file mode 100644
index 0000000000000000000000000000000000000000..64a60716cc7d056315c315480026a2b041fd4e29
--- /dev/null
+++ b/testing/input/print.in
@@ -0,0 +1,18 @@
+# You can enumerate each parameter's values as an explicit list separated by commas or by a range start:end[:step]
+# Not given parameters will receive default values
+
+# LACPY
+# nb: Tile size
+# ib: Inner tile size
+# uplo: Part of the matrix to be copied (0 for Upper, 1 for Lower and 2 for UpperLower)
+# M: Number of rows of matrices A and C
+# N: Number of columns of matrices B and C
+# LDA: Leading dimension of matrix A
+# LDB: Leading dimension of matrix B
+
+op = print
+nb = 16, 17
+ib = 8
+m = 13:45:16
+n = 15:52:16
+lda = 65
diff --git a/testing/testing_zprint.c b/testing/testing_zprint.c
new file mode 100644
index 0000000000000000000000000000000000000000..1db0422641d87723b0bd15dc75619e79f3cf1b53
--- /dev/null
+++ b/testing/testing_zprint.c
@@ -0,0 +1,132 @@
+/**
+ *
+ * @file testing_zprint.c
+ *
+ * @copyright 2019-2021 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria,
+ *                      Univ. Bordeaux. All rights reserved.
+ *
+ ***
+ *
+ * @brief Chameleon zprint testing
+ *
+ * @version 1.1.0
+ * @author Lucas Barros de Assis
+ * @author Mathieu Faverge
+ * @date 2020-11-19
+ * @precisions normal z -> c d s
+ *
+ */
+#include <chameleon.h>
+#include "testings.h"
+#include "testing_zcheck.h"
+#include <chameleon/flops.h>
+
+/**
+ *  Internal function to return address of block (m,n) with m,n = block indices
+ */
+inline static void *chameleon_getaddr_cm(const CHAM_desc_t *A, int m, int n)
+{
+    size_t mm = m + A->i / A->mb;
+    size_t nn = n + A->j / A->nb;
+    size_t eltsize = CHAMELEON_Element_Size(A->dtyp);
+    size_t offset = 0;
+
+#if defined(CHAMELEON_USE_MPI)
+    assert( A->myrank == A->get_rankof( A, mm, nn) );
+    mm = mm / A->p;
+    nn = nn / A->q;
+#endif
+
+    offset = (size_t)(A->llm * A->nb) * nn + (size_t)(A->mb) * mm;
+    return (void*)((intptr_t)A->mat + (offset*eltsize) );
+}
+
+inline static int chameleon_getblkldd_cm(const CHAM_desc_t *A, int m) {
+    (void)m;
+    return A->llm;
+}
+
+int
+testing_zprint( run_arg_list_t *args, int check )
+{
+    int          hres = 0;
+    CHAM_desc_t *descA;
+
+    /* Reads arguments */
+    intptr_t    mtxfmt = parameters_getvalue_int( "mtxfmt" );
+    int         nb     = run_arg_get_int( args, "nb", 320 );
+    int         P      = parameters_getvalue_int( "P" );
+    int         N      = run_arg_get_int( args, "N", 1000 );
+    int         M      = run_arg_get_int( args, "M", N );
+    int         LDA    = run_arg_get_int( args, "LDA", M );
+    int         l1    = run_arg_get_int( args, "l1", nb / 2 );
+    int         l2    = run_arg_get_int( args, "l2", l1 / 3 );
+    int         l3    = run_arg_get_int( args, "l3", l2 / 2 );
+    int         Q      = parameters_compute_q( P );
+
+    int list_nb[] = { nb, l1, l2, l3, 0 };
+
+    CHAMELEON_Set( CHAMELEON_TILE_SIZE, nb );
+
+    fprintf( stdout, "--- Tile layout ---\n" );
+    CHAMELEON_Desc_Create(
+        &descA, (void*)(-mtxfmt), ChamComplexDouble, nb, nb, nb * nb, LDA, N, 0, 0, M, N, P, Q );
+
+    CHAMELEON_Desc_Print( descA );
+
+    CHAMELEON_Desc_Destroy( &descA );
+
+    fprintf( stdout, "--- Lapacke layout ---\n" );
+    CHAMELEON_Desc_Create_User(
+        &descA, (void*)(-mtxfmt), ChamComplexDouble, nb, nb, nb * nb, LDA, N, 0, 0, M, N, P, Q,
+        chameleon_getaddr_cm, chameleon_getblkldd_cm, NULL );
+
+    CHAMELEON_Desc_Print( descA );
+    CHAMELEON_Desc_Destroy( &descA );
+
+    fprintf( stdout, "--- Recursive layout (Tile)---\n" );
+    CHAMELEON_Recursive_Desc_Create(
+        &descA, CHAMELEON_MAT_ALLOC_GLOBAL, ChamComplexDouble,
+        list_nb, list_nb, LDA, N, M, N, P, Q,
+        NULL, NULL, NULL );
+
+    CHAMELEON_Desc_Print( descA );
+    CHAMELEON_Desc_Destroy( &descA );
+
+    fprintf( stdout, "--- Recursive layout (Lapack) ---\n" );
+    CHAMELEON_Recursive_Desc_Create(
+        &descA, CHAMELEON_MAT_ALLOC_GLOBAL, ChamComplexDouble,
+        list_nb, list_nb, LDA, N, M, N, P, Q,
+        chameleon_getaddr_cm, chameleon_getblkldd_cm, NULL );
+
+    CHAMELEON_Desc_Print( descA );
+    CHAMELEON_Desc_Destroy( &descA );
+
+    run_arg_add_fixdbl( args, "time", 1. );
+    run_arg_add_fixdbl( args, "gflops", 1. );
+
+    return hres;
+}
+
+testing_t   test_zprint;
+const char *zprint_params[] = { "mtxfmt", "nb", "l1", "l2", "l3", "m", "n", "lda", NULL };
+const char *zprint_output[] = { NULL };
+const char *zprint_outchk[] = { "RETURN", NULL };
+
+/**
+ * @brief Testing registration function
+ */
+void testing_zprint_init( void ) __attribute__( ( constructor ) );
+void
+testing_zprint_init( void )
+{
+    test_zprint.name        = "zprint";
+    test_zprint.helper      = "Print descriptors";
+    test_zprint.params      = zprint_params;
+    test_zprint.output      = zprint_output;
+    test_zprint.outchk      = zprint_outchk;
+    test_zprint.fptr        = testing_zprint;
+    test_zprint.next        = NULL;
+
+    testing_register( &test_zprint );
+}