#include "tool.h"
 #include "header.h"
 #include "vdso.h"
+#include "probe-file.h"
 
 
 static bool no_buildid_cache;
        return ret;
 }
 
+#ifdef HAVE_LIBELF_SUPPORT
+static int build_id_cache__add_sdt_cache(const char *sbuild_id,
+                                         const char *realname)
+{
+       struct probe_cache *cache;
+       int ret;
+
+       cache = probe_cache__new(sbuild_id);
+       if (!cache)
+               return -1;
+
+       ret = probe_cache__scan_sdt(cache, realname);
+       if (ret >= 0) {
+               pr_debug("Found %d SDTs in %s\n", ret, realname);
+               if (probe_cache__commit(cache) < 0)
+                       ret = -1;
+       }
+       probe_cache__delete(cache);
+       return ret;
+}
+#else
+#define build_id_cache__add_sdt_cache(sbuild_id, realname) (0)
+#endif
+
 int build_id_cache__add_s(const char *sbuild_id, const char *name,
                          bool is_kallsyms, bool is_vdso)
 {
 
        if (symlink(tmp, linkname) == 0)
                err = 0;
+
+       /* Update SDT cache : error is just warned */
+       if (build_id_cache__add_sdt_cache(sbuild_id, realname) < 0)
+               pr_debug("Failed to update/scan SDT cache for %s\n", realname);
+
 out_free:
        if (!is_kallsyms)
                free(realname);
 
                p = strchr(buf, '\n');
                if (p)
                        *p = '\0';
-               if (buf[0] == '#') {    /* #perf_probe_event */
+               /* #perf_probe_event or %sdt_event */
+               if (buf[0] == '#' || buf[0] == '%') {
                        entry = probe_cache_entry__new(NULL);
                        if (!entry) {
                                ret = -ENOMEM;
                                goto out;
                        }
+                       if (buf[0] == '%')
+                               entry->sdt = true;
                        entry->spev = strdup(buf + 1);
                        if (entry->spev)
                                ret = parse_perf_probe_command(buf + 1,
        return ret;
 }
 
+static unsigned long long sdt_note__get_addr(struct sdt_note *note)
+{
+       return note->bit32 ? (unsigned long long)note->addr.a32[0]
+                : (unsigned long long)note->addr.a64[0];
+}
+
+int probe_cache__scan_sdt(struct probe_cache *pcache, const char *pathname)
+{
+       struct probe_cache_entry *entry = NULL;
+       struct list_head sdtlist;
+       struct sdt_note *note;
+       char *buf;
+       char sdtgrp[64];
+       int ret;
+
+       INIT_LIST_HEAD(&sdtlist);
+       ret = get_sdt_note_list(&sdtlist, pathname);
+       if (ret < 0) {
+               pr_debug("Failed to get sdt note: %d\n", ret);
+               return ret;
+       }
+       list_for_each_entry(note, &sdtlist, note_list) {
+               ret = snprintf(sdtgrp, 64, "sdt_%s", note->provider);
+               if (ret < 0)
+                       break;
+               /* Try to find same-name entry */
+               entry = probe_cache__find_by_name(pcache, sdtgrp, note->name);
+               if (!entry) {
+                       entry = probe_cache_entry__new(NULL);
+                       if (!entry) {
+                               ret = -ENOMEM;
+                               break;
+                       }
+                       entry->sdt = true;
+                       ret = asprintf(&entry->spev, "%s:%s=%s", sdtgrp,
+                                       note->name, note->name);
+                       if (ret < 0)
+                               break;
+                       entry->pev.event = strdup(note->name);
+                       entry->pev.group = strdup(sdtgrp);
+                       list_add_tail(&entry->node, &pcache->entries);
+               }
+               ret = asprintf(&buf, "p:%s/%s %s:0x%llx",
+                               sdtgrp, note->name, pathname,
+                               sdt_note__get_addr(note));
+               if (ret < 0)
+                       break;
+               strlist__add(entry->tevlist, buf);
+               free(buf);
+               entry = NULL;
+       }
+       if (entry) {
+               list_del_init(&entry->node);
+               probe_cache_entry__delete(entry);
+       }
+       cleanup_sdt_note_list(&sdtlist);
+       return ret;
+}
+
 static int probe_cache_entry__write(struct probe_cache_entry *entry, int fd)
 {
        struct str_node *snode;
        struct stat st;
        struct iovec iov[3];
+       const char *prefix = entry->sdt ? "%" : "#";
        int ret;
        /* Save stat for rollback */
        ret = fstat(fd, &st);
        if (ret < 0)
                return ret;
 
-       pr_debug("Writing cache: #%s\n", entry->spev);
-       iov[0].iov_base = (void *)"#"; iov[0].iov_len = 1;
+       pr_debug("Writing cache: %s%s\n", prefix, entry->spev);
+       iov[0].iov_base = (void *)prefix; iov[0].iov_len = 1;
        iov[1].iov_base = entry->spev; iov[1].iov_len = strlen(entry->spev);
        iov[2].iov_base = (void *)"\n"; iov[2].iov_len = 1;
        ret = writev(fd, iov, 3);
 
 /* Cache of probe definitions */
 struct probe_cache_entry {
        struct list_head        node;
+       bool                    sdt;
        struct perf_probe_event pev;
        char                    *spev;
        struct strlist          *tevlist;
 int probe_cache__add_entry(struct probe_cache *pcache,
                           struct perf_probe_event *pev,
                           struct probe_trace_event *tevs, int ntevs);
+int probe_cache__scan_sdt(struct probe_cache *pcache, const char *pathname);
 int probe_cache__commit(struct probe_cache *pcache);
 void probe_cache__purge(struct probe_cache *pcache);
 void probe_cache__delete(struct probe_cache *pcache);